Refer to the source documentation for this article
Github.com/immerjs/imm…This topic describes how to use the IMMER V3

As we all know, it is recommended to use immutable data structures when developing React projects so that React can detect data changes efficiently and correctly to determine whether or not to update the UI.

There are several libraries out there that help you implement immutable. Immutable. Js is fairly straightforward and provides methods that you can call when necessary, but I think it adds to the burden of memorizing individual apis, and most importantly, Often accidentally or smoothly or hand cheap directly modify OBJ! In fact, I think this is the key to the problem, and the emergence of IMmer is a good solution to this pain point. Because the idea is to wrap up your entire operation, whether you push the array directly or change obj. Field, you’ll end up with a new object.

API

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

The first argument prepares the object you want to change, and the second argument is a callback. The argument to this callback is a temporary object that it copies for you, so you can do anything with it. Finally produce returns the new object (the next state), and currentState remains unchanged.

The basic example

import produce from "immer"

const baseState = [
    {
        todo: "Learn typescript".done: true
    },
    {
        todo: "Try immer".done: false}]const nextState = produce(baseState, draftState => {
    draftState.push({todo: "Tweet about it"})
    draftState[1].done = true
})Copy the code

So you just have to focus on the logic, not the immutability. This is more obvious in the reducer of Redux below.

An example of reducer

After receiving new products, add these products to the total state according to THEIR ID

const byId = (state, action) = > {
    switch (action.type) {
        case RECEIVE_PRODUCTS:
            return{... state, ... action.products.reduce((obj, product) = > {
                    obj[product.id] = product
                    return obj
                }, {})
            }
        default:
            return state
    }
}Copy the code

Using Immer:

import produce from "immer"

const byId = (state, action) = >
    produce(state, draft => {
        switch (action.type) {
            case RECEIVE_PRODUCTS:
                action.products.forEach(product= > {
                    draft[product.id] = product
                })
        }
    })Copy the code

You can see how easy it is to add an ID and an object to another object. And you don’t have to deal with the default case here because producer does nothing and returns the original object.

React setState example

/** * Classic React.setState with a deep merge */ onBirthDayClick1 = () => { this.setState(prevState => ({ user: { ... prevState.user, age: prevState.user.age + 1 } })) } /** * ... But, sincesetState accepts functions,
 * we can just create a curried producer and further simplify!
 */
onBirthDayClick2 = () => {
    this.setState(
        produce(draft => {
            draft.user.age += 1
        })
    )
}Copy the code

For setState that depends on the previous value, you have to write it the first way, whereas produce can simply +=. Produce ((draft)=>{}) you don’t need to pass baseState, but use the following syntax:

The produce of Currying(take, partially evaluate)

Currying is a technique that converts a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function) and returns a new function that takes the remaining arguments and returns the result, such as func(a,b,c) becoming func(a)(b)(c), That’s what Connect looks like in Redux, remember. Also, Currying is a name.

When the first argument you pass to produce is a callback function, produce returns a prebound callback function that takes a baseState as an argument.

In the previous example, produce returns the (prevState)=>{} function, so it can be placed directly in setState. Another example:

// mapper will be of signature (state, index) => state
const mapper = produce((draft, index) = > {
    draft.index = index
})

// example usage
console.dir([{}, {}, {}].map(mapper))
//[{index: 0}, {index: 1}, {index: 2}])Copy the code

This way we can reduce the previous reducer example to less code:

import produce from "immer"

const byId = produce((draft, action) => {
    switch (action.type) {
        case RECEIVE_PRODUCTS:
            action.products.forEach(product => {
                draft[product.id] = product
            })
            return}})Copy the code

Produce generates functions that accept state as an incoming value, and produce is draft. Alternatively, you can pass a second argument to initialize state:

import produce from "immer"

const byId = produce(
    (draft, action) => {
        switch (action.type) {
            case RECEIVE_PRODUCTS:
                action.products.forEach(product => {
                    draft[product.id] = product
                })
                return
        }
    },
    {
        1: {id: 1, name: "product-1"}})Copy the code

Returns the undefined

If you explicitly return undefined, then baseState will be returned. If you really want to return undefined, you need to return a predefined token: nothing

import produce, {nothing} from "immer"

const state = {
    hello: "world"
}

produce(state, draft => {})
produce(state, draft => undefined)
// Both return the original state: { hello: "world"}

produce(state, draft => nothing)
// Produces a new state, 'undefined'Copy the code

Multiple import methods

import produce from "immer"
import {produce} from "immer"

const {produce} = require("immer")
const produce = require("immer").produce
const produce = require("immer").default

import unleashTheMagic from "immer"
import {produce as unleashTheMagic} from "immer"Copy the code

Asynchronous producer

import produce from "immer"

const user = {
    name: "michel".todos: []}const loadedUser = await produce(user, async function(draft) {
    draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
})Copy the code

performance

Package size 4.35K, speed is similar to imMutableJS