React Redux: Immutable JS

As we all probably know by now, Javascript was not built with immutability in mind. For this reason, in our reducers, we usually copy over the state using object.assign or a spread operator to copy over the current state to a new state and append to it. This method is ok UNLESS our state is very big. In big apps, when we continue appending to our state in a session, our state keeps growing and growing, keeping data we no longer need. This is where immutable JS comes in. Two main persistent immutable data structures that immutable js offers are Map (for objects) and List (for arrays).

Install

npm install --save immutable

Object/Array vs Map/List

Map

Javascript Object:

const obj = {name: 'Rochelle', surname: 'Cassar', parents: {mother: 'Pauline', father: 'France'}}

obj.name //Rochelle
obj.parents.mother //Pauline
obj.name = 'Rochelle Claire'

Immutable JS Map:

const obj = Map({name: 'Rochelle', surname: 'Cassar'})

obj.get('name') //Rochelle
obj.getIn(['parents','mother']) //Pauline
obj.set('Rochelle Claire')

List

Javascript Array:

const arr = ['Rochelle', 'Cassar']

arr[0] //Rochelle
arr[0] = 'Rochelle Claire'

Immutable JS List:

const arr = List(['Rochelle', 'Cassar'])

arr.get(0) //Rochelle
arr.set(0, 'Rochelle Claire')

In order to avoid an extra learning curve, immutable JS lists have all the normal Array methods you’re used to like size, push, pop, includes, reverse and unshift.

toJS

toJS helps us changing an immutable JS Map/List to a plain javascript object/array accordingly.

fromJS

On the contrary, fromJS helps us changing a plain javascript object / array to immutable JS Map/List.

merge

Immutable JS uses merge to merge objects together:

const objFirst = {name: 'Rochelle'}
const objSecond = {surname: 'Cassar'}
const obj = objFirst.merge(objSecond)

Immutable Reducer States

userReducer.js

import { Map, fromJS } from 'immutable'
const initialState = Map({
  isFetching: true,
  error: '',
})

export default function users (state = initialState, action) {
  switch (action.type) {
    case FETCHING_USERS :
      return state.merge({
        isFetching: true
      })
    caseFETCHING_USERS_SUCCESS :
      return state.merge({
        error: '',
        isFetching: false,
        users: action.users
      })
    default :
      return state
  }
}

In Container:

Since immutablejs has a different way of accessing properties, we need to also make sure that our mapStateToProps is using .get instead:

function mapStateToProps ({users}, props) {
  return {
    user: users.get(props.uid)
  }
}

Result:

If you use chrome redux extension or place a debugger in the mapStateToProps before the return, you can see that the state is always clean, consisting of the initialState plus the state we append in the reducer action. This was, any old state we do not need, is cleared out.

Leave a comment