Immutability through Mutability: Immer & Redux

Cloning made easy

It’s easy for things like your Redux state to turn into a nested mess. If you want to maintain immutability, you either have to use a specialized library like Immutable.JS which has its own API for manipulating objects, or you do essentially the following:

I hate writing this 😢

This doesn’t only look bad ( arrow code anyone? ) but it also feels really bad 😞. You waste so much time cloning things and if you screw up then you just lost all the benefits of immutability.

ImmutableJS solves a lot of those issues, but it has an entirely different API to use when manipulating data and it’s not always intuitive (no spread, no dot syntax, uses strings to access nested objects, etc).

You also lose a lot of the performance advantages if you convert an ImmutableJS object to plain JS. Which means you either have to use the ImmutableJS API in your React code 😫 or you need to add a memoization layer with something like reselect to keep the performance impact down😐.

The other solution — normalization is often difficult to implement, especially if your data sources aren’t on board. Transforming back and forth between normalized and nested data can be a nightmare for the uninitiated.

The Solution

A few days ago, Michel Weststrate (the creator of MobX) came out with a brilliant little library called Immer (German for “Always”). It uses ES6 Proxies on objects “produced” with it to clone modified sections and return that cloned object as a response.

What this means is that you can do all of your standard Javascript mutation operations, and Immer will keep things immutable under the hood.

Here’s the same logic as above but this time using Immer:

So much cleaner 👼

I never thought I’d be so happy to mutate something in Javascript 😄! But despite feeling slightly dirty if you’ve been doing functional programming for a while, these mutations are actually creating immutable changes under the hood, you just don’t have to worry about all the cloning.

It’s at 1600 stars and counting right now on GitHub, and it’s an approach that I think holds a lot of promise. I highly recommend checking out Michel Weststrate’s initial blog post on it.

How I use it

One minor issue not noted in the Immer documentation, is that mutation means we’re no longer returning from every case in our switch statements. This means we have to use break... which is not generally regarded as a smart move 😕.

Break is the closest thing we have today to the old goto method in assembly language programming, and there's a reason we don't expose that to modern languages.

Goto relations aside, however, it's just incredibly easy to forget. In fact, despite being aware of this, I totally forgot to put it in when I was first testing out Immer, which resulted in a few very confused minutes while I tried to figure out why nothing was working (both my creation and deletion actions were running every time, so nothing ever changed).

I’ve instead opted to use an approach similar to that described in the reducing boilerplate section of the Redux docs, and also covered extensively in this article

Since I don’t like re-writing boilerplate, I’ve created the following function:

I love extracting little things like this 😃

This does make the assumption that your actions are similar to this standard with a type and payload.

You can then use it as follows:

So much less boilerplate & cloning pain ✔️

(Simple CodeSandbox example here )

Which is a whole lot nicer than the standard spread cloning method:

I hope I never have to write this kind of thing again 😦

ImmutableJS isn’t too bad on the boilerplate side of things, but its use of strings for merging deep changes is a bit scary as your compile time checks won’t be able to help you spot errors.

Current Limitations

I’m a big fan, but there are some limitations with the library right now

  1. Proxies aren’t yet in Internet Explorer or Android’s React-Native so Immer falls back to a much less performant es5 version 😕
  2. Immer only supports Javascript objects and arrays right now. That’s 99% of what I do in reducers anyways, but you’ll have to do your own cloning if you need Maps, primitives, or something custom.
  3. If you opt not to use switch (like I have) then you do lose the nice fall-through syntax that switch statements provide.
  4. If you’ve gotten into the habit of only using immutable functions (slice instead of splice, etc) you’ll have to brush up on their mutable counterparts.

Conclusion

The concepts of immutability and the boilerplate are always blockers when I try to teach a fellow developer how to use Redux. I think Immer can finally reduce that cognitive overload when first learning, and I’m excited about its potential.

As always, feel free to comment below or tweet me @Tetheta. Let me know if this was useful to you and what you think about Immer’s “immutability through mutability.”

Written with StackEdit.
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.