Immutability with Immer in React

In React applications the most important part is the application state. We can maintain local state within the component or global state across the application using Redux. Managing the state becomes crucial as we may have inconsistent app behavior or have bugs if the state is not managed properly.

So while dealing with the state management in React we always come across a term ie. Immutability

What is Immutability?

The simplest meaning of immutability is “unable to change”. Consider we have an object and want to update it. Ideally we should copy the object and then make the changes in new object without mutating the original one.

Immutability is a design pattern where something can’t be modified after being instantiated. If we want to change its value we must recreate it with the new value instead. Some JavaScript types are immutable and some are mutable, which means their value can change without recreating it.”

While creating any applications we must consider end users and their corresponding data. Any user data, created, updated or deleted must be managed properly. We should follow better standards while managing our application data. Immutability is one of such standards we must follow for achieving the same.

Let’s have a look at benefits of following immutability

  1. Improves Readability
  2. Improves Maintainability
  3. Reduces bugs and side effects

Immutablity in React

In React we are always been told not to mutate the state directly and always use the setState. Let’s first take a look at reason behind it.

React track’s changes to the state of any component and then re-renders it in case of any changes. Now if we update the state mutably without using setState then react can’t track this change and it will not trigger the re-render. By using setState we update the state immutably assisting react to track this change and re-render the component.

Similarly in Redux, which is one of the most used state management library for React applications, immutability is used. Redux represents the application state as Immutable objects. We update the redux state using pure functions called reducers Reducers should never mutate the state. Each time the state is to be updated a new object should be created, all the changes should be made to this newly created object and then it should be returned as the updated state.

As it is evident, the concept of immutability is been widely used in React community. To handle this immutability properly we can make use of a library called immer .

Immer

To manage the state in a better way the Immer library is been used. Immer helps us to have & manage immutable state easily. Immer is based on ‘copy-on-write’ mechanism.

“Immer (German for: always) is a tiny package that allows you to work with immutable state more conveniently. It is based on the copy-on-write mechanism.”

Let’s understand first how Immer works

The basic idea is to will apply all changes to a temporary draftState, which is a proxy of the currentState. Once all mutations are completed, Immer will produce the nextState based on the mutations to the draft state. This means that you can interact with your data by simply modifying it while keeping all the benefits of immutable data.

Immer helps us as a personal assistant. It takes a letter (current state) and gives us a copy to work with. We can update and apply our changes to this copy letter(draft state). Now once all changes are done to copy letter it produces the final letter(next state).

You can add Immer to your project simply by 

yarn add immer or npm install immer

We will be using the default function exported by Immer ie. produce. Let’s have a look at produce syntax

“produce(currentState, producer: (draftState) => void): nextState”

First argument to produce is our current state and second argument is a function that gets the draft state and then performs all the required changes on draft state to produce the next state.

Let’s understand this by an simple example

Consider we have a component with local state as,

this.state = {
  user: {
    name: "",
  },
  users: []
}

And we have a handler named onInputChange which updates the user.name in local state. We need to immutably handle this state change to avoid any side effects. So we can handle this state change using immer in following way

onInputChange = event => {
  this.setState(produce(this.state, draftState => {
    draftState.user.name = event.target.value;  
  }))
}

The current state is passed to produce as a first param and the second parameter recieves the draft state which can be further used to perform the state updation. Finally the produce function will produce the next state immutably which will be then used to set the final state value.

Curried Producers

Now as we are storing the user name correctly, lets create a new user each time submit button is clicked. Let’s write a onSubmit handler that pushes the user.name to users array. Also once the user is pushed to users array clear the user name.

onSubmitUser = () => {
  this.setState(produce(draftState => {
    draftState.users.push(this.state.user);
    draftState.user.name = "";
  }))
}

If you notice in the above example, we are directly passing the function parameter which will in turn provide the draft state as parameter to this function. We are not passing the current state here which was the first parameter in our previous code example..

Immer has something called curried producersIf you directly pass a function as a single param to produce it will be used for currying and we will have a curried function. This curried function now accepts the state and calls our updated draft function.

Passing a function as the first argument to produce is intended to be used for carrying. This means that you get a pre-bound producer that only needs a state to produce the value from. The producer function gets passed in the draft and any further arguments that were passed to the curried function.

So that’s all about Immer. You can learn more in depth from the Immer Documentation.

Thank you!

One thought on “Immutability with Immer in React

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.