We’re using Redux on a project at work and a couple of months ago we decided it was time to ditch Object.assign and the spread operator in favor of an actual immutability solution for Redux.
We used the following criteria to inform our decision:
- The library should leave a small syntactic footprint. We didn’t want to pollute our code base with a noticeable amount of extra syntax and boilerplate.
- Immutability should be enforced. Redux fundamentally depends on the redux stores being truly immutable.
- Persistent data structures was a nice-to-have. The size of our data and the frequency that our data changes didn’t concern us to the point of seeking out an immutability solution which leveraged persistent data structures.
Brevity of Syntax
The brevity of seamless-immutable’s syntax is also really nice. This is best explained by example and the following example includes most of how we’re using this library.
merge() consist of the majority of “extra syntax” that we wouldn’t otherwise have, with the occasional
asMutable() sprinkled in because.. Angular (see below). Just to reiterate, every one of those operations returns an object or array that looks and quacks like an object or array respectively, with the majority of object/array methods available too.
Attempting to mutate a seamless-immutable object will throw a runtime error.
While a runtime exception isn’t the perfect answer to enforcing immutability, it at least gives us the guarantee that our immutable data will never change.
We found that we were able to sufficiently circumvent these runtime exceptions by writing unit tests for both our reducers and our selectors by using mock data that was wrapped in a call to
Immutable(). If the functions we tested threw a runtime exception we knew those function were attempting to mutate immutable data. That extra call in our unit tests was a negligible price to pay to ensure we don’t receive those runtime exceptions.
A quick caveat..
We’re using AngularJS with Redux as a state management solution. AngularJS is very opinionated and oftentimes oversteps its bounds. This is one of those times.
When you pass an array to
ng-repeat, Angular will throw its own attributes on the objects being iterated over. If the array that you give to
ng-repeat is a seamless-immutable array you’ll get a runtime exception because Angular is trying to add $$hashKey to it behind the scenes.
While that was a frustrating discovery, it’s unfair to fault seamless-immutable for Angular’s quirks and you can simply call
Persistent Data Structures
As I mentioned earlier, this characteristic was the least important to us. However, having structural sharing within an immutable object would be a bonus.
While seamless-immutable cannot and will not have persistent data structures, we ended up satisfying this wish list item after all because of Redux.
Let’s take a look at Redux to understand why.
- Redux operates on the idea that there is one giant application state driving your views.
- Redux expects you to provide a reducer function that takes the previous application state, an action, and produce a new state if a change was made. Otherwise the original state reference is returned.
- Redux reducers are composable. That is to say, I can assign smaller reducers to manage smaller parts of my state tree.
Even though our entire application tree is a seamless-immutable object, only one or two small chunks of the tree change reference for any given action. One or two reducers may return a new reference for an action, but most reducers will return the original part of the entire state they were managing. So instead of getting full blown structural sharing, we get chunk sharing where within those individual chunks there isn’t any structural sharing.
Is seamless-immutable better than ImmutableJS?
In summary, is seamless-immutable better? I don’t know. That kind of question doesn’t make sense to ask, in my opinion.
A better question would be, what are the trade-offs between these two immutability solutions?
We defined the criteria for an acceptable immutability solution and seamless-immutable came out on top. ImmutableJS is backed by a tech giant, has a large community in comparison, and uses persistent data structures, but those weren’t as important to us. From that regard, we think seamless-immutable is worth checking out as a possible alternative to ImmutableJS.