The most common Redux mistake and how to avoid it

I want to present to you the most common Redux mistake according to Redux maintainer Mark  Erikson (@acemarke):

State mutation is the #1 cause of “Why isn’t my component updating?”

You have probably experienced it yourself while implementing a new feature:

  1. You create the action creators and reducers.
  2. You connect the store to the components.
  3. Then you expect your components to update when you dispatch the action.
  4. But nothing happens…

The most common root cause of this issue is that you mutate the state in your reducers.

But what does mutate state mean?

And why can it cause bugs?

And maybe most importantly… what should you do?

Immutable and mutable data

First, a quick reminder what is mutable and immutable data.

  • Immutable data is data that cannot be changed once it has been created.
  • Mutable data is data that can be changed once it has been created.

JavaScript does not have immutable data structures built in, but we can act as if we did. Instead of changing an object directly, we can clone it and add changes to the cloned object with Object.assign or the spread operator.

Pass by reference

Before we move on, we need to learn about objects in JavaScript.

When we assign an object to a variable, what we really do is assign a reference to that object. Look at this example:

const a = {};

const b = a;

Now, a and b points to the same object.

If we mutate b, we also mutate a because they point at the same object.

a.a = "a"

console.log(b); // outputs {a: "a"} even though we haven't changed b

We can see that they are the same by doing a ===

console.log(a === b); //outputs true

If we do an Object.assign, we see that the new object we get has a new reference:

const c = Object.assign({}, a);

console.log(a === c); //outputs false

Why does the reducer require immutable data?

Ok, now we are ready to learn why Redux operates with immutable data only!

When an action has been dispatched, Redux checks if the state has changed or not. If it has not changed then it doesn’t have to update the components.

Redux checks if the state has changed by checking if the reference to the state object has changed. It looks something like this:

const hasChanged = oldState !== newState;

If you mutate the data then the reference will be the same – even though the data is changed – and Redux will not see that the state has changed. When Redux cannot see that the state has changed, it will not update the React components with new data from the state.

How to avoid bugs with immutable data

The way to “tell” Redux that the state has changed is to create a new object – which will have a new reference. We do that by cloning the old state, and adding our changes on top of the new object.

const newState = Object.assign({}, oldState, { modalOpen: true });

You can do the same with less syntax:

const newState = { ...oldState, modalOpen: true };

The ... is the ES6 spread syntax

What syntax you use is entirely up to you!

Avoid bugs by understanding Redux deeply

The best (but not the quickest) way to avoid bugs and bad code is to develop a deep understanding of the technology you are working with.

When I was learning Redux I was surprised when I realized that the source code for Redux is totally understandable – even for a beginner. I decided to code a mini version of Redux and it was only 18 lines of code.

Get one step closer to master Redux – get my free ebook by signing up below!

  • Well explained. Most of my StackOverflow answers are a variation of this mistake. There is another mistake developers make. To mutate more than what is required. (like deep cloning). In this case, there will be performance problems.

  • Chaim Lando

    But does the state object change when we use combine reducers? Because in the reducer we only change one property of the state.

    • jakoblind

      Good question! It creates a new object – even when changing only one property of the state. I wrote an article about how combineReducer works. If you haven’t checked it out already it’s available here: http://blog.jakoblind.no/code-your-own-combinereducers/

      It’s a bit simplified though. What if NO reducer change the state? Then in my implementation I will still return a new object. The real implementation handles this. Look at the source code that I link to 🙂

      • Chaim Lando

        Nice, I read both your explanation and the code. It’s actually a very simple check. Thanks for your posts, they’re great

        • jakoblind

          Awesome Chaim! Glad you like my posts 🙂

  • Iliad Moosavi

    Great read, just a quick note, there’s an extra equal sign here:
    const hasChanged = oldState !=== newState;

    • jakoblind

      Thanks, will update the post!