Don’t mutate state in react

kranthi
3 min readMar 24, 2019

--

This post describes the importance of not mutating state in react.

To understand state mutations in react, first one need to understand basics of data mutations in javascript.

If something is immutable it means it can not be modified. In javascript there are 5 primitives. numbers, strings, booleans, undefined and null, these are immutable.

Example1:-

let str = ‘abc’

str cannot be modified directly, so if we want to do modifications to the string we have to use functions like replace, toUpperCase etc. which create a new string.

Example2:-

let str1 = ‘abc’let str2 = ‘abc’

Now str1 === str2 will returns true, because both str1 and str2 have same value and refer to the same primitive.

But this does not work for objects.

let str1 = new String(‘abc’)let str2 = new String(‘abc’)

str1 === str2 returns false because although the values are equal, two different objects are created in memory, so they both refer to two different objects, so this comparison has failed in reference comparison.

In javascript, if you want to compare the values of arrays or objects there is no direct way to do, each individual elements in array or key value pairs in object need to be compared.

Problems with state mutations in react

In React docs, it’s mentioned that never change this.state directly instead always use this.setState for any state updates, these are two main reasons to do this:

a.) setState works in batches, which means one cannot expect the setState to do the state update immediately, it is an asynchronous operation so the state changes may happen in later point in time which means manually mutating state may get overriden by setState.

b.) Performance. When using pure component or shouldComponentUpdate, they will do a shallow compare using === operator, but if you mutate the state the object reference will still be the same so the comparison would fail.

Avoiding array/object mutations

a.) Use slice

let x = [‘a’, ’b’, ’c’, ’d’, ’e’]

If we want to remove c from the above array and print the array, we can do as below.

x.splice(2,1)console.log(x) // prints [‘a’, ’b’, ’d’, ’e’]

but splice directly modified x, so it has mutated the array.

This can be achieved immutably as below using slice and concat as these are immutable operations,

let x = [‘a’, ’b’, ’c’, ’d’, ’e’]
let y = x.slice(0,2).concat(x.slice(3))
console.log(x) // prints original array [‘a’, ’b’, ’c’, ’d’, ’e’]
console.log(y) // prints [‘a’, ’b’, ’d’, ’e’]

b.) Use Object.assign

let x = { ‘a’:’Hello’, ‘b’: ‘Hey’ }

Now let’s say if we want to change the value of ‘a’ from ‘Hello’ to ‘Hurray’.

Mutable way:-

x.a = ‘Hurray’, this will directly modify the object which we want to avoid in react if x belongs to state.

Immutable way:-

let y = Object.assign({}, x }// creates a brand new object

y.a = ‘Hurray’, now y can be used to update the react state as this is completely immutable.

c.) Use Spread operator in ES6

The same functionality as in above can be achieved using spread operator.

let x = { ‘a’:’Hello’, ‘b’: ‘Hey’ }let y = {…x,’a’:’Hurray’}console.log(x) // prints { a: ‘Hello’, b: ‘Hey’ }console.log(y)// prints { a: ‘Hurray’, b: ‘Hey’ }

d.) Nested Objects

Let’s say state contains user object which looks as below,

let user = {         profile:{              address:{              city: ‘London’              }         }}

If we want to modify city from London to Newyork immutably, we need to do it as below

{…state,    user:{        …state.user,        profile:{…state.user.profile,        address:{…state.user.profile.address, city:’Newyork’}}    }}

This is why it is recommended to keep react state as flat as possible, also consider using Immutable.js ( https://facebook.github.io/immutable-js/ ) which makes immutable data modifications using inbuilt functions or Immutability Helper as suggested in React docs ( https://reactjs.org/docs/update.html ).

--

--