ReactJS/Redux - Pure vs Impure Javascript functions?

React and Redux both need pure functions coupled with immutability to run in a predictable fashion.

If you don't follow these two things, your app will have bugs, the most common being React/Redux not able to track changes and unable to re-render when your state/prop changes.

In terms of React, consider the following example:

let state = {
    add: 0,
}

function render() {
    //...
}
//pure function
function effects(state,action) {
//following immutability while updating state, not directly mutating the state.
    if(action == 'addTen') {
        return {...state, add: state.add + 10} 
    }
    return state;
}

function shouldUpdate(s) {
    if(s === state){
        return false
    }
    return true
}

state = effects(state, 'addTen')if(shouldUpdate(state)) {
    render();
}

The state is held by the state object which has only added property. This app renders the app property. It shouldn't always render the state when anything happens but should check whether a change occurred in the state object.

Like so, we have an effects function, a pure function which we use to affect our state. You see that it returns a new state when the state is to be changed and returns the same state when no modification is required.

We also have a shouldUpdate function which checks using the === operator whether the old state and the new state is the same.

To make mistakes in terms of React, you can actually do the following :

function effects(state,action) {

  doRandom(); // effects should only be called for updating state.
             // Doing any other stuff here would make effects impure.

    if(action == 'addTen') {
        return {...state, add: state.add + 10}
    }
    return state;
}

You can also make mistakes by setting the state directly and not using effects function.

function doMistake(newValue) {
    this.state = newValue
}

The above should not be done and only effects function should be used to update the state.

In terms of React, we call effects as setState.

For Redux:

  1. Redux's combineReducers utility checks for reference changes.
  2. React-Redux's connect method generates components that check reference changes for both the root state and the return values from mapState functions to see if the wrapped component actually needs to re-render.
  3. Time-travel debugging requires that reducer be pure functions with no side effects so that you can correctly jump between different states.

You can easily violate the above three by using impure functions as reducers.

Following is taken directly from redux docs:

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).
It's very important that the reducer stays pure. Things you should never do inside a reducer:

Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().

Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.


Simply said the state cannot be mutated. A new instance of the state should be returned every time there is a change so

This code is not correct :

const initialStates = {    
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {
        state.items.push(action.item)
        return state
    }
    default:
      return state
  }
}

This code when written as a pure function below, This returns a new instance of the array it does not modify the actual array itself. This is the reason you should use a library like immer to handle immutability

const initialStates = { 
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {

        state = {...state,items:state.items.concat(action.item)}
        return state
    }
    default:
      return state
  }
}