Immer - Javascript Immutability the happy way
Tags: react, tutorial, javascript
Published: 2020-01-10 10:16:41

**Prerequisite: Basic knowledge about [React](https://reactjs.org/) and Immutability in Javascript**

In this post, I am going to talk about [Immer](https://github.com/immerjs/immer), a library that makes immutability in Javascript way easier and simpler.

I assume that you already know why we need immutability. If you don't, no worries, check [this blog](https://itnext.io/why-concept-of-immutability-is-so-damn-important-for-a-beginner-front-end-developer-8da85b565c8e) first 😍

**🍹 Spoiler alert**

If you want to make a mixed drink, pour wine and sodas into a glass, not the sodas into the bottle of wine. We call it **Immutable Bottle of Wine**

> I let you decide why we should do that

![immutable](https://user-images.githubusercontent.com/26871154/71762599-d2583b00-2ed9-11ea-8edf-3df8bae51e1e.jpg)

💪 Let's get started!

## 1. Immutability in Javascript

Back to the first time I learned React, I only know one way to make the state immutable and I bet you are familiar with it too

Yes, you are absolutely right. Let's talk about ...

### ⭐ Spread operator

Our task today is to make a mixed drink for the New Year. 

>Discount 50% for anyone reading this blog

**Our Happy Menu**

🍷 The infamous mutable bottle of wine

One day, our new bartender got drunk, so he poured the sodas into the bottle of wine. Therefore, that bottle of wine was spoiled badly ⚠️

Next day, he used that wine bottle to mix other drinks to serve the guests. Of course, other drinkers did not realize that it is no longer the original drink but they could spot after tasting it 🐛🐛

```jsx
const bottleOfWine = ['wine']

function mixWineAndSoda(bottleOfWine) {
  
  bottleOfWine.push('soda') // Opps, he spoiled the bottle of wine with sodas
}

mixWineAndSoda(bottleOfWine)

console.log(bottleOfWine) // ['wine', 'soda']
```

We modified the `bottleOfWine` array by accident when we put it into the `mixWineAndSoda` function. Imagine that we use this `bottleOfWine` in many functions and keep modifying it. It's really hard to debug and keep track of which function adding what to the `bottleOfWine` and what if we want to use our original array 🙃

🍹 The famous immutable bottle of wine

This drink is only for experienced coders who want to learn the correct way to mix wine and sodas

```jsx
const bottleOfWine = ['wine']

function mixWineAndSoda(bottleOfWine) {
  
  // pour wine from bottle into a glass
  const wineGlass = {...bottleOfWine}
  
  // add soda
  wineGlass.push('soda')
  
  return wineGlass
}

const mixedDrink = mixWineAndSoda(bottleOfWine)

console.log(bottleOfWine) // ['wine']
console.log(mixedDrink) // ['wine', 'soda']
```

By making a copy of `bottleOfWine` then modify it, we prevent ourselves from imutating our original array

![immutable](https://user-images.githubusercontent.com/26871154/72139658-d8499280-3397-11ea-87b7-f719bdc06222.png)


🤫 **Spread operator** is really cool. However, it could be painful when it comes to really nested object 

Let's do a small task: Change the address of our bar from `Paris` to `New York` without mutate the `barInfo` object

```jsx
const barInfo = {
  address: {
    country: {
      city: 'Paris'
    }
  }
}
```

🤫 Honestly, I struggled to do this task. Thanks to Netflix and The Witcher for helping me 

>🎶 Toss a coin to your Witcher. A friend of humanity

```jsx
const updatedBarInfo = {
  ...barInfo,
  address: {
    ...barInfo.address,
    country: {
      ...barInfo.address.city,
      city: 'New York'
    }
  }
}

console.log(barInfo.address.country.city) // Paris
console.log(updatedBarInfo.address.country.city) // New York
```

<img src="https://user-images.githubusercontent.com/26871154/71765421-304a4a00-2efd-11ea-801e-84b5476ddfe4.jpg" width="600" title="Cry in pain"/>

### ⭐ ImmutableJS

There are other ways to achieve immutability including **Object.assign** or [ImmutableJS](https://github.com/immutable-js/immutable-js). However, I find it complicated to use ImmutableJS as we have to learn and understand the whole new API to use it. 

Let's take a quick look 🙄

```jsx
import {fromJS} from 'immutable'

const barInfo = fromJS({
    address: {
        country: {
            city: 'Paris',
        },
    },
})

const updatedBarInfo = barInfo.updateIn (
    ['address', 'country', 'city'],
    value => 'New York',
)

console.log(barInfo) //Map {size: 1, _root: ArrayMapNode, ...}
console.log(barInfo.toJS().address.country.city) // Paris

console.log(updatedBarInfo) //Map {size: 1, _root: ArrayMapNode, ...}
console.log(updatedBarInfo.toJS().address.country.city) // New York
```

As you can see, we have to wrap the `barInfo` object within `fromJs` function to make it immutable. We then use `updateIn` to modify the city value. Note that `barInfo` is no longer a normal Javascript object, it becomes **Immutable.Map**. To turn it back to normal Javascript object, we have to use `toJS()`. 

And that's just a small part of **ImmutableJS API**

We have to learn the whole new API to use **ImmutableJS** effectively 👽

## 2. Immer in Javascript

All you need to remember is that **Immer** has a `produce` function that allows us to create a draft. By modifying the draft, we avoid mutating the original object.

> produce(currentState, producer: (draftState) => void): nextState
  
💪 Let's take a look at our example  

First, we wrap our object or array within the `produce` function then we can modify the draft without the fear of mutating the original object/array.

```jsx
import produce from 'immer'

const bottleOfWine = ['wine']

function mixWineAndSoda(bottleOfWine) {

  const wineGlass = produce(bottleOfWine, draft => { // draft is our glass
    draft.push('soda') // add soda
  })
  
  return wineGlass
}

const mixedDrink = mixWineAndSoda(bottleOfWine)

console.log(bottleOfWine) // ['wine']
console.log(mixedDrink) // ['wine', 'soda']
```

**Immer** shows its magic when it comes to nested object since we can modify the draft as the way we do with normal javascript object or array

```jsx
import produce from 'immer'

const barInfo = {
  address: {
    country: {
      city: 'Paris'
    }
  }
}

const updatedBarInfo = produce(barInfo, draft => {
    draft.address.country.city = 'New York' 🔥
})

console.log(barInfo.address.country.city) // Paris
console.log(updatedBarInfo.address.country.city) // New York
```

> 📢  Happy new year to everyone

## 3. Immer in React:

In React applications, we normally want to make sure our state is immutable. 

Let's see how **Immer** works in React application

### 🔥 Immer with Producer in Redux State

In this example of Redux State, we want to update the value of `label` from `Cocktail` to `Martini` without mutating our original state. We can achieve that using **Spread operator**

```jsx
const initialState = {
    data: {label: 'Cocktail'},
    isLoading: false
}

const reducer = (state = initialState, action) => {
    switch(action.type) {
        case CHANGE_LABEL:
            return {
                ...state,
                data {
                    ...state.data,
                    label: 'Martini'
                }
            }
    }
}
```

💪 Let's use Immer to simplify our reducer

> produce(currentState, producer: (draftState) => void): nextState

```jsx
import produce from 'immer'

const initialState = {
    data: {label: 'Cocktail'},
    isLoading: false
}

const reducer = (state = initialState, action) => {
    return produce(state, draft => {
        switch(action.type) {
            case CHANGE_LABEL:
                draft.data.label = 'Martini'
                break       
        }
    })
}
```

We use `produce` function to wrap our original state and then modify the `draft`. The `produce` function automatically returns a new state for us if we updated the `draft`. 

### 🔥 Immer with Curried Producer in Redux State

We can even make it simpler by using **Curried Producer** 💪

If you work with functional programming, you will be familiar with the **Currying** concept. I will not cover the functional programming concepts here and if you don't work with functional programming, you can just accept the **Curried Producer** as a new syntax.

⚠️ With **Curried Producer**, the `state` is omitted and the `initialState` is passed as a second argument of `produce` 

💪 Normal Producer
```jsx
import produce from 'immer'

const reducer = (state = initialState, action) => {
    return produce(state, draft => {
        switch(action.type) {
            case CHANGE_LABEL:
                draft.data.label = 'Martini'
                break       
        }
    })
}
```

💪 Curried Producer
```jsx
import produce from 'immer'

const reducer = produce(draft, action) => {
   switch(action.type) {
    case CHANGE_LABEL:
        draft.data.label = 'Martini'
        break       
   },
   initialState
}
```

You may ask what if you want to get the original state within the `produce` since the `state` is omitted. [original](https://immerjs.github.io/immer/docs/original) comes into rescue 😎

```jsx
import produce, {original} from 'immer'

const reducer = produce(draft, action) => {
   switch(action.type) {
    case CHANGE_LABEL:
        original(draft.data) // In case you really want to get data from the original state
        draft.data.label = 'Martini'
        break       
   },
   initialState
}
```

### 🔥 Immer in Component State

I will go through really quick without much explanation since it is the same as we've discussed above. However, I want to introduce you the `use-immer` library

In our example, we use **React.useState** hook for state management and we can update the state via `updateBottleOfWine` function

💪 Normal producer
```jsx
import React from 'react
import produce from 'immer'

const App = () => {
    const [bottleOfWine, setBottleOfWine] =  React.useState(['wine'])
    
    function updateBottleOfWine() {
        setBottleOfWine(state => produce(state, draft => {
            draft.push('sodas')
        })
    }
}
```

💪 Simplify with Curried Producer

Pay attention to `updateBottleOfWine` function to see how we omit the state

```jsx
import React from 'react
import produce from 'immer'

const App = () => {
    const [bottleOfWine, setBottleOfWine] =  React.useState(['wine'])
    
    function updateBottleOfWine() {
        setBottleOfWine(produce(draft => { //👈
            draft.push('sodas')
        })
    }
}
```

💪 Simplify with [use-immer](https://github.com/immerjs/use-immer)

We use `useImmer` instead of `React.useState` then we can just update the state directly without worrying about mutating the original state.

```jsx
import React from 'react
import {useImmer} from 'use-immer'

const App = () => {
    const [bottleOfWine, setBottleOfWine] = useImmer(['wine']) // 👈
    
    function updateBottleOfWine() {
        setBottleOfWine(draft => {
            draft.push('sodas')
        })
    }
}

```
## 4. Conclusion:

**Immer** is a Javascript library that makes immutability way simple. By using **Immer**, we can find it easy to modify nested objects without the fear of mutating it. It is very straightforward to use **Immer** as we can modify object or array as the way we used to, without having to adopt the whole new API. 👏👏👏

Here are some good resources for you:
- [Why Immutability is so important](https://itnext.io/why-concept-of-immutability-is-so-damn-important-for-a-beginner-front-end-developer-8da85b565c8e)
- [Easy immutable objects in Javascript](https://spapas.github.io/2018/04/05/easy-immutable-objects/)
- [Immutable JavaScript Data Structures with Immer](https://egghead.io/courses/immutable-javascript-data-structures-with-immer)
- [ImmutableJS Docs](https://immutable-js.github.io/immutable-js/docs/#/)


## 🙏 💪 Thanks for reading!

*I would love to hear your ideas and feedback. Feel free to comment below!*

### ✍️ Written by

Huy Trinh 🔥 🎩 ♥️ ♠️ ♦️ ♣️ 🤓

Software developer | Magic lover

Say Hello 👋 on

✅ [Github](https://github.com/HuyAms)

✅ [LinkedIn](https://www.linkedin.com/in/huydinhtrinh/)

✅ [Medium](https://medium.com/@trnhnhhuy)