• What is a mutant state?
  • Why does React not recommend the mutation state?
  • How to update an object properly?

In short, a Render phase normally starts when props and state change, which is ideal

Because React knows: “Oh! I need to get back to work.”

What is a mutation?

When we give setState a base data type, the value of state will be immutable

When updated, the original value of state is not changed, but an immutable base data type is recreated to trigger rerendering

const [num,setNum] = setState(0)
// render
setNum(10)
Copy the code

What if we give setState an object?

Technically, you can change the contents of the object itself, which we call mutation

const [name,setName] = useState({age:18})

name.age = 20
Copy the code

However, the React state is technically mutable, and it is still recommended to use immutable methods to change the React state

Like primitive data types, always replace them, not change them

whyreactMutant status is not recommended

  • Debug: If you use console.log and do not change the state, your past days will not be corrupted by the latest state, and you can clearly see the state changes between renders

  • Optimization: If the previous props and state are the same as the next state, the usual React optimization strategy will skip the render. If you never change the state, checking the changes will be very block-like. If prevProps === props, react confirms that it hasn’t changed internally

  • New feature: The new feature being built in React relies on treating state as a snapshot, which will render the new feature unavailable if you’re updating a past version of state

  • Requirement changes: Some values that require undo/redo and display history are easier to perform without mutations because you can keep past values in a copy and redo them if applicable

  • Simpler implementation: Because React doesn’t rely on mutations, it doesn’t need to do anything to your objects and doesn’t need to hijack them. Always wrap them in agents, or do something else at initialization as many “reactive” solutions do. This is also why React allows you to put any object (no matter how large) into state without additional performance or correctness pitfalls.

How to update an object properly?

We can use immer to encapsulate a hooks

How does immer work?

A draft provided by IMmer is a special type of object called a Proxy. It “records” what you do with it. That’s why you can change it at will! Internally, Immer finds out which parts of the Draft have been changed and generates a brand new object containing your edits.

import produce, { Draft } from 'immer'
import { useState } from 'react'

// Define a type alias that converts each key of the object to readonly
// Instead of actually marking the object as read-only, constraints are placed on type usage
type DeepReadonly<T> = T extends object
    ? {
        readonly [k in keyof T]: T[k] extends { [Key: string]: unknown } ? DeepReadonly<T[k]> : T[k]
    }
    : never

export function useImmutable<T extends object> (val: DeepReadonly<T>) :DeepReadonly<T>,cb: (recipe: Draft<DeepReadonly<T>>) => void) = >void] {

    const [immutable, setImmutable] = useState<DeepReadonly<T>>(val)

    function updateImmutable(cb: (updateImmutable: Draft<DeepReadonly<T>>) => void) {
        // Create a new IMMUTABLE state (nextState) by updating imMUTABLE via cb callback
        // We cannot manipulate immutable because we package produce
        const nextState = produce(immutable, (cloneImmutable) = > {
            cb(cloneImmutable)
        })
        // The converted nextState is used for updates
        setImmutable(nextState)

    }

    return [immutable, updateImmutable]
}

const [profile,UpdateProfile] = useImmutable<{name:string({} >name:"as"})

profile.name = "error Update" // A type error occurs. The object is read-only

/ / update
UpdateProfile((nextState) = > {
   nextState.name = 'update'
})
Copy the code

Reference:

  • Update objects in state