Introduction
If you’ve used Redux at some point, theres a good chance you’ve heard of or even used Immer. Immer was created as a way of making Redux reducers easier to deal with.
When React moved from classes to functions, the useState and useReducer were actually inspired by Redux and as a side effect, immer became something we could use with hooks.
Unfortunately, most developers had Redux-Burnout, so most people haven’t touched immer since then, preferring to “keep things simple” with useState. However, sometimes you still end up with complex objects/arrays inside of a useState and end up doing some things that would be considered “weird” if you weren’t using react.
Imagine you’re using a table library (e.g. @mui/x-data-grid-pro) that has you pass in an array of columns
(headers) and rows
(data). Now lets say you’re building a fintech app that has a toggle to display transactions in usd-$ and euros-€. To do this in react we’ve have to do something like this. Since when we call a useState
setter, the array/object passed in needs to have a different reference, we need to use .map
to create a copy of our array.
This code is alright, but its very “react-y” and can get a lot more complicated and annoying to write. Which is where Immer comes in,
This feels a lot more natural. We’re just “mutating” the draft (proxy) object, which takes care of translating the “mutation” operations into immutable operations.
Not only is this more natural, Immer also makes the code you write simpler. In my DataGrid example, we can actually take it a step further.
In a more complex example, this might actually make some performance gains.
Immer was so impactful that it even won some awards: React OS awards, JS OS Awards. Libraries like Solidjs (which won its own JS OS award) even integrated immer’s produce API into their Store design.
Fun Fact 1
Immer is life-changing as a JS dev, and I’m not even exaggerating :) Like, it’s right up there with Prettier in terms of “wow this package is amazing, how did I ever live without it?” —Mark Erikson, (the) Redux Maintainer
Fun Fact 2
Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way.
Use cases
Immer
The most common use case of immer to to avoid destructuring both objects and arrays:
Example from the Immer docs
Nested Data example from Immer Docs
See more Common Update Patterns
The useImmer Hook
If you plan on using immer heavilty and want to avoid having to call produce all the time, theres actually a useImmer
utility hook that works just like you’d expect:
- Works like useState when setting a value e.g.
setState(42)
- Works like produce in the callback:
setState(draft => { draft.value = 42 })