Shallow comparison in Redux
machy44
Posted on April 10, 2019
One day at work I was trying to implement a new feature in my React/Redux project. I created a new reducer and asked myself if I took reducers as pure functions for granted. I was creating reducers as a pure function and I have never really asked myself why it must be pure (I admit I'm ashamed). I thought the only reason for this is to accomplish the undo history option in redux easier.
In this blog, I will try to explain why reducer shouldn't mutate the app's state. I will use the spread operator syntax.
Shallow comparison
Every time when you change something in the state you need to create a new object.That new object will have a new address in the memory. It means that we will pass an object by value, not by reference. You can see in the next JS code what does this mean.
//by reference
let first = {a: 1};
let second = first; // second shows on the same space in memory as the first
first.a = 2;
first === second; // shallow comparison will log true
console.log(first); // {a: 2}
console.log(second); // {a: 2}
//by value
let firstValue = {a: 1};
let secondValue = {...firstValue}; // we pass firstValue by the value
firstValue === secondValue; // shallow comparison will log false
firstValue.a = 2;
console.log(firstValue); // {a: 2}
console.log(secondValue); // {a: 1}
In Redux, a reducer is a function which does a certain job (it changes the app's state ). You can see this in an example below.
const initialState = {
data: [],
isError: false,
isLoading: false
}
function exampleReducer(state = initialState, action) {
switch (action.type) {
case REQUEST_API:
return { ...state, isLoading: true }; //creating a new state object
case RESPONSE_API:
return { ...state, isLoading: false, data }; //creating a new state object
default:
return state;
}
}
Redux does the shallow comparison:
oldState === newState; // true or false
If a change has happened in the state (a new object is created) false value will be returned. This way, React/Redux knows if a component re-rendering needs to be triggered. It's more efficient to check this than doing a deep comparison or something else.
Sometimes we could encounter with deeply nested objects which can be hard to update. In this situation you can normalize the app's state or you can use immutable-focused libraries such as Immutable.js. Maybe I could write about this in some future posts.
Conclusion
In the end, it is easier to test pure functions.
It's always good to look at things under the hood and try to understand why things are the way they are.
If you have some thoughts to share or I missed something fell free to comment.
Posted on April 10, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.