Tutorials‎ > ‎

Immutable Data with Immutable.js

posted Dec 17, 2017, 7:03 PM by Benedictus Jason Reinhart   [ updated Feb 26, 2018, 1:11 AM ]
"In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change but the object's state appears to be unchanging from an external point of view." (Wikipedia)

If you have read somewhere that immutability is important and such use cases are in your current JS project, Immutable.js is probably what you are looking for. The library itself does not have any dependencies, so you can just load it independently without any additional package managers such as npm, even though it is still recommended to use build tools such as webpack to ship Immutable.js.

Immutable.js can be included in your pages simply with a <script src="Immutable.js"></script> or using npm (npm install immutable).

In modern web applications, many operations are done asynchronously such as fetching data and event handling. These operations can be confusing as you cannot really predict when things will happen and what data should you expect within an operation. With immutable objects, things become predictable and consistent across your application.

Moreover, some Javascript data structure APIs are inconsistent. For example, Array.prototype.map and Array.prototype.filter in Javascript ES5 does not mutate the current array, but Array.prototype.sort, Array.prototype.shift and Array.prototype.unshift mutate the array. These kinds of inconsistencies may lead to bugs and confusion. To anticipate these we can use Immutable.js.

First, add Immutable.js to your page.
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js"></script>

Then, start constructing an immutable array.
// using javascript plain array
var numbers = Immutable.List([1, 2, 5, 4, 3]);

Now, the inconsistencies in the plain Javascript array are gone, and all kind of operations on the array will result in a brand new array.
var sortedNumbers = numbers.sort();
console.log(numbers); // logs [1, 2, 5, 4, 3]
console.log(sortedNumbers); // logs [1, 2, 3, 4, 5]
console.log(numbers === sortedNumbers); // logs false
// while in plain Javascript the operations will result in numbers and sortedNumbers being the same object reference.

We can also try the same with filter and shift.
var oddNumbers = numbers.filter(n => n % 2 == 1);
var shiftedNumbers = numbers.shift();
console.log(numbers); // [1, 2, 3, 4, 5]
console.log(oddNumbers); // [1, 3, 5]
console.log(shiftedNumbers); // [2, 3, 4, 5]

Here, the numbers can be manipulated many times without altering the object (array). We filtered the numbers, then shifted it, but numbers will always be the same. In a component based app, sometimes there's a parent-child relationship between components, and usually the parent passes the data to the child. However, this may lead to bug in the parent component if the children try to alter the data passed from the parent, especially if the passed data is a reference such as objects and arrays. With immutable data, the parent should not be concerned if the data passed to the child will be manipulated or not.

We can also use Immutable.Map for objects and maps. The concept is the same as objects and maps, just a slightly different usage.
First, construct an Immutable.Map:
var person = Immutable.Map();
person.set('name', 'Bob');
person.set('age', 15);
person.set(Immutable.List([1, 2, 3], 'Immutable list can be used as keys in Immutable.map too');
console.log(person.get('name')); // "Bob"
console.log(person.get('age')); // 15
console.log(person.get(Immutable.List([1, 2, 3])); // "Immutable list can be used as keys in Immutable.map too"

Working with immutable objects might complicate things such as comparing 2 objects. As "mutating" an object in Immutable.js creates a new object, comparing with == and === will not work properly as you expected. Thus, Immutable.js provides us a utility function, is() which is a global function. Example:
var numbers = Immutable.List([1, 2, 5, 4, 3]);
var positiveNumbers = numbers.filter(n => n >= 0);
console.log(numbers == positiveNumbers); // false
console.log(numbers === positiveNumbers); // false
console.log(is(numbers, positiveNumbers)); // true

So, remember when using Immutable.js to compare with the is() function provided, not == or ===.