Tag Archives: Immutable.js

Using Immutable.js .update() instead of .get() then .set()

Often you need to deeply update an complex Map or List. Using Immutable.js .update() is a powerful way to this kind of update.

Given this state

const = myState = Immutable.Map({
'123': Immutable.Map({
id: 123,
b: false,
}),
'456': Immutable.Map({
id: 456,
b: true,
}),
});

Don’t use .get() then .set()

const myUpdatedItem = myState.get('123').set('b', true);
const myNewState = myState.set('123', myUpdatedItem);

Using .get() then .set() seems simple and clean, but it’s slow, it fully replaced the the current Map. While the results are the same, ImmutableJS cannot optimize against it and cannot make some faster assumptions.

Using Immutable.js .update()

const myNewState = myState.update('123', (myMap) => {
return myMap.set('b', true);
});

It is much better to using Immutable.js .update(). Immutable can keep more references and make assumptions that allow it to optimize the update. For more, check the .update() docs.

You can also use .updateIn() for deeply nested data. Read my post on Immutable get() vs .getIn() to see how it works.

Comparing Immutable.Set() and Immutable.OrderedSet()

When comparing Immutable.Set() and Immutable.OrderedSet() you’ll need to convert the OderedSet down to just a plain Set.

Comparing Immutable.Set() and Immutable.OrderedSet()

To compare equality with a Set and OrderedSet use this:

mySet.equals( myOrderedSet.toSet() )

Immutable .equals() compares the left and right to decide if they are both Ordered. If one is ordered and the other is not, they are not equal. isOrdered(a) !== isOrdered(b).

My Feelings

I find this a bit odd, especially when the left is a plain Set. I feel regardless if the right is Ordered or not, it’s the contents that we’re interested in comparing. However this is the Immutable.js definition. You will need to downgraded the OrderedSet for comparison via .toSet() which does have a slight cost to it. Immutable performance is worth keeping an eye on.

Immutable.js .get() vs. .getIn()

At Sprout Social in some areas of our frontend app we use Immutable.js for our Redux store.

Standardizing Selector Styles.

When selecting state out of our store we’ve written a collection of selectors to consolidate selectors logic. We’ve always write these selectors in array notation to keep styles consistent.

messageStore.getIn([id, 'author', 'screenname']);

With all selectors written in array notation, for Immutable.js we use .getIn() by default — regardless if the path is only one key deep. It’s very convenient. Keeps our selectors looking consistent in shape.

messageStore.getIn([id]);

Immutable.js .get() vs. .getIn()

However it is always faster to do a .get() instead of a .getIn() .  For  .getIn() Immutable.js has to iterate through an array path and check the result for each key path along the way. This therefore makes .getIn() expensive and .get() ultimately cheaper.

Abstract .getIn()

So if we wish to keep all selectors a consistent array path style, but take advantage of the speed of.get() whenever possible we could make a get abstraction. The first thing to do is to create an abstraction for Immutable .getIn() — something like this:

Add .get() to the abstraction.

How could we get this handy abstraction to take advantage of .get() whenever possible?
We could peak into the arrayPath , if there is only one value, use plain old .get(). Seems simple enough.

But is it performant?

Array length checking is cheap. With a quick little JSPerf test we can see immediately takins the time to check if we should use  .get() is more than twice as fast than just always using .getIn().

https://jsperf.com/immutable-get-getin-path-check/1

In the end

In the end, this is a micro optimization. However, we saw ~50ms to ~150ms speedups for every actions in the app. It really depends on the how often your selectors run. We have a large number of selectors that fire quite a bit. For such a small change, we’ll gladly take any performance boost over 100ms.

Don’t worry about the abstraction, just remember to prefer.get() over .getIn() whenever possible.

Pro tip: Same goes for .set() / .setIn() and .update() / .updateIn(), etc.