What-is: Immer – Immutability through mutations

Immer was created by the same person who created Mobx. Immer is an amazing library to help us work with immutable data more easily.

How Immer works

  • Your actual state data
  • Immer —→ creates a COPY of – which is a new temporary ‘draft’ that would be used as a proxy of the next state data.

Why is this important?

So in React whenever you are modifying a state or when working with Redux, you don’t want to mutate the existing object, BUT rather, just create a COPY of that existing object. And it is the COPY that you would be modifying or working with, not the original data.

How to use Immer?

$ npm install immer  

or 

$ yarn add immer 

Next, we would import Immer inside our component. The library also exports a default function called produce.

import produce from "immer" 

Let’s create a function component using immer and call its default function, which is named produce. Then, we pass in the current state as the first attribute, while the second parameter is another function that will receive the “temporary draft” of the new version of how you want the data to look like.

const heroName = (hero, name) => {
	return produce(hero, draft => {

  {/* this here will basically modify the hero as if 
	we were mutating the original object, heroName. */}  

	draft.name = name; 	

}) 
}; 

Yes, this is probably an overkill way of using Immer, but Immer shines best when you are using more complicated and nested data.

So let’s take a look a more slightly complex example.

Lets say we have an array of Heroes and we want to add to that array another hero.

MUTABLE WAY

const addHero = (heroes, hero) => {
	heroes.push(hero);
	return person; 
}

By using push, we are mutating the array by pushing into it a new element.

IMMUTABLE WAY IN ES6

const addHero = (heroes, hero) => {
	return [...heroes, hero]; 
};

IMMER WAY

const addHero = (heroes, hero) => {
	return produce(heroes, draft =>    {
		draft.push(hero);
});
}

ES6 IMMUTABLE WAY WORKING ON NESTED DATA OR OBJECTS:

const changeJob = (hero, job) => {
	return {
	...hero, 
	company: {
		...hero.job, 
		job
	}
};
};

Yes, this gets the job done of NOT mutating your original data or object, but as you can see, it’s very icky and verbose, especially when it starts to get deeper.

Now, let’s see the immer way when it comes to nested values.

IMMER WAY WORKING ON NESTED DATA OR OBJECTS:

const changeJob = (hero, job) => {
	return produce { hero, draft => {
				draft.company.job = job;

})
};

As we can see, using a library like Immer makes our code easier and simpler on its own in the Javascript land.

👉🏼 But using Immer within a React app gets even better, especially around the setState that is also being used in nested objects or data.

WITHOUT IMMER: this is typically how we would write it:

First, we need to extract the value or details from the state.

setEventName = value => {
	{/* extract the value or details from the entire state*/} 

	const {datails} = this.state;
	this.setState({

	{/* get a copy of the old state plus the modified eventName or value */} 
	details: {...details, eventName: value}
 });
}

USING IMMER WITH NESTED DATA OR OBJECT

First, we would start by calling setState and pass it the produce function coming from Immer.

setEventName = value => {
	this.setState(produce(draft => {
			draft.details.eventName = value; also 

}))
};