ShakaCode | ShakaCode Blog | Rails On Maui Blog | Rails | ReactJs | JavaScript | Webpack | Productivity |

Immutable.js, Lodash, React, Redux


#1

I just came across a great article by James Long.

Using Immutable Data Structures in JavaScript

JavaScript Interop

The major downside to Immutable.js data structures is the reason that they are able to implement all the above features: they are not normal JavaScript data structures. An Immutable.js object is completely different from a JavaScript object.

That means you must do map.get(“property”) instead of map.property, and array.get(0) instead of array[0]. While Immutable.js goes to great lengths to provide JavaScript-compatible APIs, even they are different (push must return a new array instead of mutating the existing instance). You can feel it fighting the default mutation-heavy semantics of JavaScript.

The reason this makes things complicated is that unless you’re really hardcore and are starting a project from scratch, you can’t use Immutable objects everywhere. You don’t really need to anyway for local objects of small functions. Even if you create every single object/array/etc as immutable, you’re going to have to work with 3rd party libraries which use normal JavaScript objects/arrays/etc.

The result is that you never know if you are working with a JavaScript object or an Immutable one. This makes reasoning about functions harder. While it’s possible to be clear where you are using immutable objects, you still pass them through the system into places where it’s not clear.

Property/Variable Naming to Indicate Immutable

One way that we’re solving this with our own projects is to prefix all property and var names that point to Immutables with $$:

const $$map1 = Immutable.Map({a:1, b:2, c:3});
const $$map2 = map1.set('b', 50);
$$map1.get('b'); // 2
$$map2.get('b'); // 50

This makes it clear that this would be a mistake:

const map = Immutable.Map({a:1, b:2, c:3});
map['b'] = 50;

So far, our team loves using $$ to clearly show which properties and vars are immutable. This is working out well in a large React + Redux project.

Another big gotcha of using Immutable.js is:

To sum it all up: JavaScript interop is a real issue. Never reference JavaScript objects from Immutable ones.

By having the naming convention, if we had code like this, it would clearly be an issue:

const $$map1 = Immutable.Map({a:1, b:2, c:3});
const someJsObject = {d: 4, e: 5}; // NOT IMMUTABLE, regular JS Object
const $$map2 = map1.set('$$someJsObject', someJsObject); // NOTE: `set` always returns a new Immutable object

So by reading the code, I see that I’m assigning someJsObject to “$$someJsObject”. Once you get used to this pattern, you can immediately see that this is incongruent.

Another interesting point in the article is the mention of transit-immutable-js:

Transit is also much smarter about how it encodes types. For example, it knows that map keys might be complex types as well, so it’s easy to tell how it how to serialize Map types. Using the transit-immutable-js library (referenced above) to support Immutable.js, now we can do things like this:

let { toJSON, fromJSON } = require('transit-immutable-js');

let map = Immutable.Map();
map = map.set(Immutable.Map({ x: 1, y: 2 }), “value”);

let newMap = fromJSON(toJSON(map));
newMap.get(Immutable.Map({ x: 1, y: 2 })); // -> “value”

However, most of the time we need to serialize and deserialize of when going to/from AJAX calls. So I’m not sure how this fits in with that.

@alexfedoseev, maybe you can add more to this discussion? I bet we can present a few more good examples, and maybe turn this into an article.


#2

transit-immutable-js solved the problem of re-hydration Immutable data from server to client. Here is my use case. On Node server I receive request. Before any render I have to fetch the data from API and put it into the server’s store. Store contains Immutable data structures. When store is filled I can render the views and send rendered html to the client. With this html I should send serialized data from server’s store to the client’s store to avoid duplicated request from client to API. When the store contains simple JS objects and arrays I can serialize it with native JSON methods, but when my store contains Immutable structures — I need a transit-immutable-js's toJSON method to serialize data on server and fromJSON method to deserialize it on the client -> recreate immutable store without duplicated request.


#3

I just published this little library (24 LOC) which gives you an immutable version of the lodash set and unset functions lodash-redux-immutability