Using Immutable.js with Redux

John Tucker
Jul 20, 2017 · 8 min read

Contrary to my initial thinking, it turns out that using Immutable.js with Redux is not that complicated and is a good practice.

Image for post
Image for post

This article assumes that you are fairly familiar with the Redux library; if not, I wrote a series of articles on the topic starting with:

I later wrote an article describing why I didn’t use Immutable.js with Redux.

At the end of the article, however, I concluded that the situation was more complicated than I originally thought. In this article I continue to explore the topic through a couple of examples.

To build (skip if you already did this in the Redux By Example series).

  1. Install Node.js.
  2. Download and extract the repository Redux Patterns
  3. From the extracted folder, run npm install
  4. Run the command ./node_modules/.bin/babel src -d dist

Mutable

Before we introduce Immutable.js, let us explore the problem of using Redux without it.

From the extracted folder, run the example with the command:

Looking at this example’s source code:

  • The state consists of two leaves, byId and ids; used to store an array of objects (items) with primitive properties.
  • The code is divided into sections: reducers, selectors, action creators, store, and exercising.

Let us walk through the exercising section side-by-side with the example’s output (the remaining sections are a fairly straightforward Redux implementation).

We first dispatch a FETCH action to initialize the state.

At this point, the example’s output is (reflects an action occurred and that items has changed).

We then output the current value of the mango item.

state = store.getState();
let mango = getItem(state, 'm');
console.log('BEFORE UPDATE ACTION');
console.log(mango);
...

with expected result

We then properly update the mango item.

As before, the example’s output is (reflects an action occurred and that items has changed).

We then output the current value of the mango item.

with the expected result of

We now improperly update the mango item (basically forgetting to dispatch the UPDATE action).

Unlike the proper update, the code that reflects an action occurred and that items has changed, did not run.

We then output the current value of the mango item.

We can see, however, that the state indeed has changed.

The crux of the problem is that we were able to directly manipulate the state without using actions, i.e., UPDATE. With this, the code that is listening for changes in the state never detects the change in items.

note: In the case of using Redux with React (and React-Redux) a connected component (to items) would not re-render; leaving the user-interface out-of-synch with the state.

Immutable

Let us refactor the last example with Immutable.js following the guidance provided by the Redux team.

We first install the Immutable.js library along with Redux-Immutable (provides a Immutable.js aware combineReducers function). From the command line in the example folder I ran:

Then I updated (commented out old code and inserted new code) the example’s source code as follows:

First, I updated the imports.

Then I used the Immutable.js Map type to replace the JavaScript object for the byId leaf of the state.

Likewise, I used the Immutable.js List type to replace the JavaScript array for the ids leaf of the state.

As the entire state now consists of Immutable.js objects, we have to adapt the selectors with get methods. As per the guidance, the selectors return Immutable.js objects.

In this example, we will deviate from the guidance for simplicity; we will revisit in next example.

Using Immutable.JS everywhere keeps your code performant. Use it in your smart components, your selectors, your sagas or thunks, action creators, and especially your reducers.

— Redux Team

While we did use Immutable.js objects in the selectors and reducers, by leaving the action creators unchanged, their value properties are not Immutable.js objects.

From the extracted folder, run the example with the command:

As before, let us walk through the exercising section side-by-side with the example’s output.

As the action creators remain unchanged and the selectors have the same signature, the FETCH and OUTPUT CURRENT VALUE sections remain the same as the last example.

At this point, the example’s output is (reflects an action occurred and that items has changed).

Followed by the current value of the mango item (notice that it is now an Immutable.js Map object).

Because the mango item is an Immutable.js Map object, we need to use the set method to change the description. And because the action creators don’t use Immutable.js objects we need to use the dreaded to toJS method.

toJS() is an expensive function and negates the purpose of using Immutable.JS. Avoid its use.

— Redux Team

As before, the example’s output is (reflects an action occurred and that items has changed).

With the OUTPUT CURRENT VALUE section as before, we get the expected result of.

Because mango is an Immutable.js Map object, the set method without the UPDATE action…

…has no effect on the state (reflected in the OUTPUT CURRENT VALUE section output). This result differs from the last example and is the primary benefit of using the Immutable.js library with Redux.

Immutable-Fixed

By following the guidance and using Immutable.js in the action creators we can get rid of the expensive toJS function. One issue, however, is that the action creators in the previous examples rely on the normalizr library (unaware of Immutable.js).

Turns out that removing the normalizr library from the action creators only makes the reducers marginally more complicated.

Looking at the updated code; we got rid of the normalizr import and usage. We also will need the fromJS function from immutable.

/*
const itemSchema = new schema.Entity('items');
const itemsSchema = new schema.Array(itemSchema);
*/
...

Without the normalizr library and with action creators using Immutable.js, the byId reducer is somewhat more complex.

The ids reducer, however, is only slightly different.

The selectors remain unchanged and the action creators are simplified:

As the action creators now expect Immutable.js objects, we use fromJS to convert the array of objects to a List of Maps for fetch.

As mango is already an Immutable.js Map, we can simply pass it directly to the update action creator; in doing so, getting rid of the expensive toJS use).

Conclusion

While I am still new to using Immutable.js, having written this article has:

  • One, confirmed that on my next React / Redux project I need to use it.
  • Two, provided me confidence that using it does not complicate things too much.

Frontend Weekly

It's really hard to keep up with all the front-end…

John Tucker

Written by

Broad infrastructure, development, and soft-skill background

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

John Tucker

Written by

Broad infrastructure, development, and soft-skill background

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store