DEV Community

Cover image for Importance of not mutating state in redux/react
James Won
James Won

Posted on

Importance of not mutating state in redux/react

Recently I found myself in a situation where I faced a bug caused by a mutated reducer. This led me down a rabbit hole until finally being able to find the cause.

"Mutating state in React is an anti-pattern".

Most of us who use React know this and grudging accept this.

But there are serious consequence to not making your React/Redux code immutable.

My trip down the rabbit hole

A codebase that I was working on had a weird bug. The component I was making relied on a redux state object. But updating this state by calling an existing reducer to update the state didn't re-render the object.

I pulled my hair out for the next hour trying to get to the bottom of it. I dug deep into the theory that it had to be something to do with my component internals.

But then after a long time, I decided to change tack. What if the global state was the problem? I checked Redux DevTools and the state clearly changed. But no re-render.

I began to suspect that the React object checking was not picking up the changes - but how could that be if the state was changed? I did a small experiment using useEffect

useEffect(() => {}, 
    console.log('Is this working?')
[myStateValue])
Enter fullscreen mode Exit fullscreen mode

This did not fire when the state was updated and I had my smoking gun.

I checked the reducer updating this state. And there it was staring at me, the existing reducer was mutated 😱.

state.value = newValue
return {
    state
}
Enter fullscreen mode Exit fullscreen mode

This is an easy mistake to make

This code predated my time working on the codebase by a long time. I had assumed, incorrectly that all reducers should be set up to be immutable.

But I realised that this was a mistake that could easily be made. The tricky thing is that the code technically works - but with HUGE caveats (re-rendering bug that I faced is one of them).

It can be because people forget, or during refactoring to simplify code. Setting the value directly is something that many programmers are used to. To my horror after looking through the existing code there were more mutated state code.

What the reducer code should have looked like

return {
    ...state,
    value: newValue
}
Enter fullscreen mode Exit fullscreen mode

There is a subtle difference in the code, but the world of difference in how this is checked by React/Redux.

Why you should never mutate state

Firstly, Redux warns against mutating state. There are many, many reasons why. But the main one is that Redux uses shallow equality checking and NOT deep equality checking.

Secondly, you really shouldn't be mutating object state at all in React. React uses [.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) reference checks. This means that it is checking whether the object reference is the same in memory and NOT whether it has different nested values. React Hooks does a really good job of preventing you from doing this, but with Redux reducers the job is left to you.

// The problematic code above is like coding:
const [value, setValue] = useState('a')
state.value = 'b'

// instead of coding:
setValue('b')
Enter fullscreen mode Exit fullscreen mode

For my issue it took me an hour to find the issue, but less than a minute to fix it.

Takeaway

Never ever mutate React and Redux state!

If you find old code that does mutate code - get a plan in place to address it quickly or else it can create a debugging nightmare.

Top comments (0)