Understanding updateIn & keyPath in ImmutableJS
August 10, 2016ImmutableJS is
a neat library, especially when used in combination with Redux. I’m not ashamed to
admit, though, that I found the docs confusing at first. For example,
look at the method signature for Map#updateIn():

updateIn() is a useful function to use when you’re interacting with Redux. I had to understand it. In the end, what helped me become productive with updateIn() was understanding the keyPath parameter.
The key (sorry) to understanding keyPath is to realize it’s just an
array of values that Immutable will call get with as it works it’s way through whatever data
structure you’re calling updateIn() on. It doesn’t matter if the value is a Map key or a List index. This works because both Map#get
and List#get inherit from Iterable#get
(https://facebook.github.io/immutable-js/docs/#/Iterable/get).
Consider the following data structure, which is a Map of endpoints, keyed on their id. The values are a List of Maps that represent that endpoint’s “subscriptions” to a
given event (enabled: true|false):
const endpoints = fromJS({
"abc-123": [
{
id: 111,
endpoint_id: "abc-123",
event_key: "signup_success",
enabled: true,
},
{
id: 222,
endpoint_id: "abc-123",
event_key: "signup_failure",
enabled: false,
},
],
"def-456": [
{
id: 333,
endpoint_id: "def-456",
event_key: "signup_success",
enabled: false,
},
{
id: 444,
endpoint_id: "def-456",
event_key: "signup_failure",
enabled: true,
},
],
});
To get all the subscriptions for a given endpoint, we use Map#get as
follows:
subscriptions = endpoints.get("abc-123");
keyPath = ["abc-123"]
Since subscriptions is now a List, we can get the first element using List#get, passing it the index of the element we want to access:
subscription = subscriptions.get(0);
keyPath = [0]
We could do this in one shot:
subscription = endpoints.get("abc-123").get(0);
keyPath = ["abc-123", 0]
What’s neat is that we can construct a keyPath down to an attribute of
an element. Say we wanted to get the enabled attribute of the above
endpoint:
enabled = endpoints.get("abc-123").get(0).get('enabled');
keyPath = ["abc-123", 0, "enabled"]
Now that we know how to construct a keyPath to any point in a deeply
nested data structure, we can move to understanding the updater parameter of updateIn.
The simplest way to understand updater is that it’s a function
that will be passed as it’s parameter the result of calling get on the
final element in the keyPath array. It should return the new value for
that…value. It’s easier to show in code.
Let’s say I wanted to flip the enabled flag for the subscription
represented by element 0 for endpoint abc-123:
endpoints.updateIn(["abc-123", 0, 'enabled'], isEnabled => !isEnabled);
In the above example, I constructed the keyPath right down to the
enabled flag (the first parameter), and the second parameter is a
function that will be passed the current value of enabled as a parameter called isEnabled, and will simply return opposite boolean value of isEnabled.
You could back the keyPath up one level, as ["abc-123", 0], which
would give you the entire subscription Map at that index, and then
return a new Map with the enabled flag flipped. But in my
(limited) experience, I’ve not updated more than one attribute of a
given object at a time in a reducer. Hope this helps!
You can discuss this post at Hacker News.