Immutable.js is an awesome library for creating Immutable collections of data, hugely popular for React/Redux development, but not blessed with great documentation. Struggle no more, with this first in a series of in-depth tutorials, complete with live coding examples.
What is Immutable.js?
Set objects, but with the significant difference that all methods to add, delete or update data in a collection do not mutate (i.e. change) the collection being acted upon.
For example, Immutable’s
push() method, which adds an item of data to an Immutable
List, actually adds the item to a new copy of the List, leaving the original List collection completely unchanged.
push() method acting on (and mutating) the array itself:
In contrast, the Immutable equivalent shows the original
List completely unchanged:
// Immutable.js non-mutating List.push const collection = Immutable.List.of('ironMan'); collection.push('captainAmerica'); // Output: collection;
In this example,
push() method makes a copy of
collection and pushes the new item onto the copy, leaving
collection itself completely unchanged.
To see the result of the
push() method, we need to assign a new variable to it, as
push returns the copy.
// Immutable.js non-mutating List.push const collection = Immutable.List.of('ironMan'); const newCollection = collection.push('captainAmerica'); // Output: newCollection;
Getting your head around Immutable collections
user object, for example, containing properties such as
age, etc. These values could change at any time.
With Immutable collections, however, the values of such properties can never change. How do you visualise this, let alone act on it?
There are two ways that might help make this concept more concrete.
Think of Immutable data as a value
An immutable collection should be thought of as a value, such as a number. A number never changes. The number 7, for example, is always 7. If you add 1 to 7 you get a new value (i.e. 8 – you knew that, right?!), but that doesn’t change 7 itself – it’s still 7.
Going back to our collection examples above, an Immutable collection is no different from a number. We can add an item to our collection, but that doesn’t change the original collection itself. Indeed, we can think of the collection’s
push() method, which adds an item to an existing collection, as being equivalent to an addition operation on a number, which adds a new value to an existing number. In both cases, the original value – the collection and the number – are left completely unchanged, and a new value is returned.
This is the essence of Immutable data.
Think of Immutable data as representing the state of data
Another way to think of an Immutable collection is to think of it as the state of its data at the specific point in time that the collection was created. Whenever we query that collection, we always get the state of its data that existed at its moment of creation. We might move on in time, but the collection itself never does.
So whenever we query a collection to retrieve its data, we’re not saying to the collection “give me your data now”, we’re actually saying “give me your data as it existed when you (i.e. the collection) were first created.”
It’s the difference between asking “Who is the President of the United States right now” – which obviously depends on when you ask the question – and “Who was the President of the United States on August 13th 2016” – which is a fact that will never change.
Accordingly, any operation you perform on the data within an Immutable collection (e.g. add or remove an item) will change the state of that data at a later point in time, but the state of the data as it existed before the operation remains unchanged. What you’re left with after the operation is two collections – the first represents the state of the data before the operation, and the second represents the state of the data after the operation.
The following example may help to clarify this:
// state1 = the original state of the data at the time of // its creation: time 0 const state1_time0 = Immutable.List.of('ironMan'); // state2 = the new state of the data at a later time (time 1) // i.e. after a push operation has been performed const state2_time1 = state1_time0.push('captainAmerica'); // state1_time0 never changes, as it always reflects the state // of the data at time 0 state1_time0;
How to perform operations on Immutable collections
We’ve seen how to think of Immutable data; now we need to know how to operate on it. Immutable is a powerful library for manipulating our collections of data in a safe, immutable way (the clue’s in the title!).
But it’s not a small library, and has many edge cases that can trap the unwary. Because of this, performing operations on Immutable collections is not something that can be explained easily in a single blog post. Well, not in a short blog post anyway!
So here’s a collection of in-depth blog posts, each focused on one part of the Immutable API.
All Immutable.js Tutorials in this Series
- The Foolproof Guide to Creating Lists
- Get, Set and Delete data from an Immutable List
- Merging Lists Finally Explained
- Every Way to Create an Immutable.js Map
- Get, Set, Delete and Update data from an Immutable Map
- 6 Ways to Merge Maps in Immutable.js
P.S. I originally concluded this post with a bit of a rant about the quality of Immutable’s documentation and its reliance on auto-generated docs, which have few examples and API signatures written in TypeScript.
However, I clearly offended a few people in the Open Source Community, which was never my intent, so I’ve removed the rant. The current issues some people face regarding the documentation (and what I was complaining about) can be found here, here, here, and here (and doubtless other places as well!).