Why isn't my React component updating (using Redux)?
Why doesn’t my React component update after the action is dispatched?
You have done everything right! You know that.
You see that the action is dispatched successfully. And still the component is not updating. Your useSelector
is subscribing to the value in the store, but its value is not updated at all.
Never mutate the state in your reducers
There are many reasons why things could have gone wrong here. But the most common reason is that you mutate the state in your reducers.
You should never mutate the state in your reducers.
But what does mutate the state mean?
And why can it cause bugs?
And maybe most importantly… what should you do?
I’ll cover it all in this article, but let’s start from the beginning with some basics (or just scroll down if you want a quick solution to your problem)
What is immutable and mutable data
First, a quick reminder of 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.
In Javascript we can define an immutable variable with the const
keyword:
const myImmutableVariable = 3
myImmutableVariable = 4 // this will give an error
And we can define a mutable variable with the let
keyword:
let myMutableVariable = 3
myMutableVariable = 4 // this is ok
However, JavaScript does not have immutable data structures built in. What does that mean?
const myDatastructure = {}
myDatastructure.test = 3 // this is allowed
See that we add a new element to our object even though we defined it with the const keyword. The data was mutated.
The reason is that Javascript does not support immutable data structures.
However, there are many ways around this. You can clone the variable and add changes to the cloned object with Object.assign
like this:
const myNewDatastructure = Object.assign({}, myDatastructure, { test: 3 })
or use the spread operator, like this:
const myNewDatastructure = { ...myDatastructure, test: 3 }
In both cases, we get a new variable, myNewDatastructure
, which contains a copy of myDatastructure
with a new field test
with the value 3
. We have not mutated myDatastructure
.
Pass by reference
Before we move on, we will need to learn about one more concept: pass by reference.
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!
The Redux state is just a regular JavaScript object.
const state = {
someData: "some value",
}
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.
How does Redux check if the state has changed?
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!
Or you can use redux toolkit
Redux toolkit is an official toolset that the creators of Redux recommend using. It’s designed to be more user-friendly and remove some of the common errors and boilerplates.
With Redux Toolkit you can use mutable data in your reducers. The reason is that it uses immer under the hood.
If you want to try it out, you can get started with Redux Toolkit here.
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 mastering Redux - get my free ebook by signing up below!