ImmutableJS is an implementation of a few persistent data structures. If your ReactJS app is running slow because of constant re-rendering and costly shouldComponentUpdate, then ImmutableJS can come to the rescue.

The problem with shouldComponentUpdate

Let's assume that a React component (and has a lot of other nested components within) gets passed a prop like:

{
    "prop1": {
        "nested-key-1": "nested-value-1",
        ...
    },
    ...
    "prop2543": {
        ...
    }
}

It's getting passed a lot of data. If you attempt to use shouldComponentUpdate here against a normal JS hash, then you'd have to recursively check every key and value of the old and new props. This is a very expensive comparison.

Also, you cannot just do a oldProps == newProps because that only compares address, which is bound to be different each time.

This leads to every component within the hierarchy re-rendering when the parent gets new props, because there is no way to know for sure whether they changed. This can lead to problems like the page freezing.

Enter ImmutableJS

To understand the theory behind ImmutableJS, you'd have to learn about persistent data structures. However, I'm not going to be covering that here. Instead, I'll show you how ImmutableJS can be useful within a shouldComponentUpdate.

First, let's look at things without ImmutableJS in place

Consider the following code:

var currentProps = {
    name: {first: 'John', last: 'Rogers'},
    company: {name: 'Acme, Inc', location: 'Budapest'}
};

var serverResponse = {
    name: {first: 'John', last: 'Rogers'},
    company: {name: 'Acme, Inc', location: 'Budapest'}
};
var newProps = serverResponse;

We have a component structure like this:

  1. RootComponent (that receives props)
    1. UserComponent -> only takes the name attribute and renders it
    2. CompanyComponent -> only takes the company attribute and renders it

If in the above code, we try checking currentProps === newProps, we'll get false although the content are the same for both variables. Basically, we're using the serverResponse (which represents a response to an AJAX call) as-is for the new props.

What happens when we use ImmutableJS to track changes?

Consider the following code:

// You've already stored current props
var currentProps = Immutable.fromJS({
	name: {first: 'John', last: 'Rogers'},
  company: {name: 'Acme, Inc', location: 'Budapest'}
});

// Then you get a server response
var serverResponse = {
	name: {first: 'John', last: 'Rogers'},
  company: {name: 'Acme, Inc', location: 'Budapest'}
};

// You get newProps by merging this with old props
var newProps = currentProps.mergeDeep(serverResponse);

The following would the output that you'd get for various comparisons:

  1. currentProps === newProps => true
  2. currentProps.get('name') === newProps.get('name') => true
  3. currentProps.get('company') === newProps.get('company') => true

Basically, we had ImmutableJS merge the server response with the current props. It went through the nested hash and saw that nothing changed, so none of the memory addresses changed.

Rather than doing expensive comparisons in your shouldComponentUpdate method, you do it once when you get the server response. Then each of your component can do a simple memory address comparison using ===, and be sure that it'd come out to be equal if the underlying values have not changed.

Thus, none of the components would have to re-render.

What happens when we do change a value?

Consider the following code, which is almost identical to the above. Except in this case, we've changed the company location from Budapest to Amsterdam in the serverResponse

// You've already stored current props
var currentProps = Immutable.fromJS({
	name: {first: 'John', last: 'Rogers'},
  company: {name: 'Acme, Inc', location: 'Budapest'}
});

// Then you get a server response, wherein only the company
// location has changed to Amsterdam from Budapest
var serverResponse = {
	name: {first: 'John', last: 'Rogers'},
  company: {name: 'Acme, Inc', location: 'Budapest'}
};

// You get newProps by merging this with old props
var newProps = currentProps.mergeDeep(serverResponse);

The following would the output that you'd get for various comparisons:

  1. currentProps === newProps => false
  2. currentProps.get('name') === newProps.get('name') => true
  3. currentProps.get('company') === newProps.get('company') => false

Here, the root prop comparison returns false, because something changed. Thus the RootComponent would trigger re-rendering.

However, the name part of the data is still the same, so the NameComponent doesn't have to re-render. The CompanyComponent will have to re-render because the comparison returned false, and we did change the location.

Conclusion

The idea behind this post was not to teach you details about ImmutableJS. That's something you'd be able to pick up yourself now that you understand one of its most useful applications.

Make sure you read about and understand the concept of persistent data structures. Instead of doing heavy shouldComponentUpdate comparisons a lot of times while rendering, you do it once when updating the data. This way, your application feels fast because you've cut down on unnecessary re-rendering.