Shallow comparison in Redux

machy44 - Apr 10 '19 - - Dev Community

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.

. . . . . . . . . . . . .