All Posts
How to use Immer with your reducers

August 9, 20202 min read

As you know React’s state has to be immutable. Otherwise, we are more likely to face some unwanted side effects when we try to mutate the state directly.

An immutable object is an object whose state cannot be modified after it is created.

Therefore, If you want to use standard JavaScript data structures to produce immutable states and to reduce the boilerplate code on your reducers then immer (url) is tailored for you 💪

What we use so far when it comes to update our state in the reducers is the use of the spread operator.

Below you’ll find an example of what I mean:

const initialState = {
    data: { color: 'red' }
}

const reducer = (state = initialState, action) => {
    switch(action.type) {
        case SET_COLOR:
            return {
                ...state,
                data {
                    ...state.data,
                    color: 'yellow'
                }
            }
    }
}

Is the above code considered outdated? Of course not! But when it comes to complex data structures and huge projects then the reducers are not simple like that one and they end up having a lot of boilerplate code making them hard to read.

This is where immer comes in!

How it works

The magic lies to the produce function that immer has which allows us to create a draft object as a copy of our state but not the state itself. By modifying the draft object, we avoid mutating the original object.

The produce function automatically returns a new state for us if we updated the draft. The advantage is that we can modify the draft as the way we do with normal javascript object or array.

Installation

You can install it by the following command:

npm install immer

Then, you can import it to your application:

import produce from 'immer';

Usage

import produce from 'immer';

const initialState = {
    data: { color: 'red' }
}

const reducer = (state = initialState, action) => {
    return produce(state, draft => {
        switch(action.type) {
            case SET_COLOR:
                draft.data.color = 'yellow'
                break;  
        }
    })
}

Isn’t it way easier?

Advanced example:

Mutating an object by adding the color id as the key of the color object.

import produce from 'immer;

export default produce((draft, action) => {
  switch (action.type) {
    case ActionTypes.PopulateColors: {
      draft.items = {};

      action.payload.colors.forEach((color) => {
        draft.items[color.id] = color;
      });

      break;
    }
  }
}, defaultState.colors);