Immer is a tiny package that allows you to work with immutable state in a more convenient way.
The basic idea is that you will apply all your changes to a temporary
draftState, which is a proxy of the
currentState. Once all your mutations are completed, Immer will produce the
nextState based on the mutations to the
draftState. This means that you can interact with your data by simply modifying it while keeping all the benefits of immutable data.
We write a lot of boilerplate when using Redux and updating our store. Every developer familiar with even remotely complex reducers has seen this pattern before:
All that code just to set one entry to
completed: true! This “extra” destructuring work is required because Redux needs us to return a complete copy of the state.
What if we could just modify the entry we want, and Redux could figure out the rest? Well, that may be expecting too much of Redux, but luckily, we can now use Immer!
With Immer, the above code would look like:
From a developer point of view, I think the benefits are obvious. We’re no longer spending time trying to destructure and then restructure state. We’re just performing a simple operation and moving on with our lives.
To begin with, what does an Immer function even look like?
Here’s an example:
Okay, let’s do something a little more realistic. Let’s pretend we’re building an app where users can check a certain field. A todo list or grocery shopping app would make sense.
I think you’re probably starting to get the idea. But let’s get into why this would work for advanced applications where you’re using and abusing Redux heavily.
Here is a real example of a very painful piece of code that we had written at work. We have a UX flow that allows the user to change the
height of a displayed chart. The only thing this action handler wants to do is update the
height property of a specific chart. But unfortunately, we have to fully recreate the state via destructuring - and it gets ugly!
Ugh! That is pretty gross. Let’s use Immer instead.
Boom! The exact same functionality as above.
(If this example doesn’t win you over, I have nothing to offer you.)
Guess which one is easier to understand? Guess which one doesn’t take 15 minutes and two developers to write? (True story).
Let’s refactor another action handler - this one deletes a single entry in the store.
Is that super dope or what? We accomplish the same task with two lines of code.
Immer does have a downside - namely, it’s about twice as slow as a well-written reducer. Immer recommends not using this library when operating on tens of thousands of objects - otherwise, the performance difference is neglible.
Use Immer wherever it makes sense to you. It’s easy to start with, and the developer benefits will become clear immediately. Your functions will also become easier to test and debug, since you’ll be rid of the noise that comes along with re-creating state all the time!
For those of you using TypeScript - the Immer library is strongly typed! Very cool!