Clean NgRx reducers using Immer

Tim Deschryver

This weeks post is inspired by another great This Dot Media event and the topic this time was state management. There was a small segment about Immer which I found interesting (video is linked at the bottom of this post), so I decided to give it a shot with NgRx.

Why Immer link

Like the title says, Immer can reduce the complexity of reducers.

Ease of use, you don’t have to learn a new API or concept because it’s just using normal JavaScript objects and arrays. This can also lower the transition from a backend role to a front end role.

The thing I like most is that it can just be plugged in wherever needed. Introducing Immer doesn’t mean you have to use it in every reducer in your code base.

About Immer link


Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way. It is based on the copy-on-write mechanism.

With Immer you’re treating your state inside reducers as what they call a draft. This draft can be mutated “in the normal way”, with the JavaScript API. The mutations applied on the draft produce the next state. All this while still having the benefit that the state will remain immutable in the rest of your application. To make this possible Immer relies on Proxies under the hood.
In this post we’re only using it with a reducer, but it also can be used outside of one.

It is created by Michel Weststrate, the owner of MobX.

Side by side comparison link

Let’s take a look at what a normal NgRx reducer might look like and compare it with the Immer implementation. As an example we’re going to use the classic shopping cart example where we can add and remove items from the cart.

First off, the state, which looks as follows:

A typical NgRx implementation doesn’t mutate the current state and uses the spread syntax to return a new version of the state.

For our cart example, it looks like this:

Now let’s take a look at the Immer way.
The state remains the same and the reducer becomes:

Let’s highlight some points from the snippet above:

And some subtle differences which you may have missed:

To give another example, let’s take a look at how we load the catalog:

Pretty straight forward, right?

Usage link

In order to use Immer you’ll have to install it first via npm install immer, and then import it with import produce from 'immer’.

Good to know: selectors link

If you’re afraid that changing state like this means that every part of your application will be re-rendered, don’t be. Immer uses structurally shared data structures which basically means that only the modified parts of your state will trigger a new result from selectors based of the modified state. Thus, it only re-renders your application where needed.

Good to know: object freezing link

Immer comes with object freezing out of the box in development. This means that it will throw an error if the state is mutated from outside the produce function. So if you’re currently not using a library like ngrx-store-freeze and if you’re mutating state outside a reducer, you will get an error thrown at you.
If you still want to mutate your state (which I don’t recommend by the way), you can turn off this feature with setAutoFreeze(false).
If you build your application in production mode, this check will automatically be skipped for performance reasons.

Good to know: redux devtools link

Since we’re just using normal JavaScript objects the redux devtools just keep working.

Gif of redux devtools

we’re seeing the redux developer tool in action

Conclusion link

Simply put, I like it. But this doesn’t mean I’ll use it everywhere (hint, there is no such thing is a silver bullet), only in places where I see fit. Partly because I like (and I’m used to) writing my reducers in a functional way. But I’ll use it when a reducer becomes too bloated or too hard to reason about.


NOTE: When dealing with collections and CRUD actions you probably (still) want to use @ngrx/entity.

This post is meant to be a short introduction to Immer and to spread the word to the NgRx community. If you like what you’re seeing and want some more details or if you’re interested on how it’s working there are some useful resources below.

The code can be found on GitHub or directly on StackBlitz.

More resources link

Not to miss link

Angular contributor day


Keep up with the advancement of prominent open source frameworks, libraries, and browser standards by attending this online event. Core team members will discuss topics such as upcoming releases, recent milestones, and community initiatives.

Incoming links

Feel free to update this blog post on GitHub, thanks in advance!

Join My Newsletter (WIP)

Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at PayPal logo

Share this post on

Twitter LinkedIn