Immer编写简洁的更新state逻辑

react官网推荐库use-immer:https://www.npmjs.com/package/use-immer
引入:import { useImmer } from "use-immer";

优点:

  1. 简化代码: 只需要关注需要变动的部分,而 immer 本身将在后台处理其余部分。
  2. 学习成本和替换代价小

一、对象

假如现在有一个嵌套结构的对象:

const personList = {
  name: 'Niki de Saint Phalle',
  artwork: {
    title: 'Blue Nana',
    city: 'Hamburg',
    image: {
        url: 'https://i.imgur.com/Sd1AgUOm.jpg',
        alt: 'test',
    }
  }
}

正常写法

const [person, setPerson] = 
    useState(personList)

setPerson({
  ...person, 
  artwork: { 
    ...person.artwork, 
    image: {
      ...person.artwork.image,
      alt: 'test1'
    }
  }
});

使用use-immer:

const [person, updatePerson] = 
    useImmer(personList)

updatePerson(draft => {
  draft.artwork.image.alt = 'test1';
});
  • draft:一种特殊类型的对象,Proxy,可以保留一份之前的对象
  • Immer 找出哪些部分draft已被更改,并生成一个包含您的编辑的全新对象。

二、数据的双向绑定:immer中的核心 api ——produce

Immer.jsmobx 的作者写的一个 Immutable(不可变数据) 库,核心实现是利用 ES6 的proxy,几乎以最小的成本实现了JavaScript的不可变数据结构。

Immer编写简洁的更新state逻辑_第1张图片

api介绍:

produce(baseState, recipe: (draftState) => void): nextState

基本概念:

  • baseState:被操作对象的最初状态
  • draftState: 根据currentState生成的草稿、是currentState的代理、对draftState所有的修改都被记录并用于生成nextState。在此过程中,currentState不可变
  • recipe:用于操作draftState的函数
  • nextState: 根据draftState生成的最终状态
  • produce: 用于生成nextState或者producer的函数
import produce from "immer"

const baseState = personList

const nextState = produce(baseState, draftState => {
    draftState.artwork.image.alt = 'test1';
})

draftState的修改最终都会体现在nextState,但并不会修改baseState
需要注意的是nextStatebaseState共享未修改的部分。通过produce生成的nextState是被冻结的(使用Object.freeze实现,仅冻结nextStatecurrentState相比更改的部分),直接修改nextstate会报错。

2.1 Proxy 对象

用于创建一个对象的代理,实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
Proxy 对象接受两个参数,

  • 第一个参数:需要操作的对象
  • 第二个参数是设置对应拦截的属性,支持getset(劫持了对应元素的读和写),能够在其中进行一些操作,最终返回一个 Proxy 对象实例
const handle = {     
    get(target, key) {       
        // 这里的 target 就是 Proxy 的第一个参数对象
        console.log('proxy get key', key)       
        return '返回1'     
    },     
    set(target, key, value) {       
        console.log('proxy set key', value)     
    }   
}   
const target = {a:1}   
const p = new Proxy(target, handle)   
p.a = 2 // 所有设置操作都被转发到了 set 方法内部 

2.2 produce

源码:

Immer编写简洁的更新state逻辑_第2张图片

主流程主要根据base创建draft对象、执行用户传入的recipe拦截读写操作,走到自定义的gettersetter最后再解析组装结果返回给用户。

2.3 hook——useImmer

use-Immer库把上述setState+produce的用法封装起来。
它接收一个初始状态,返回一个数组。
数组解构出的第一个值为当前状态,第二个值为状态更新函数(produce 中的 recipe )。

“一”中修改后的:

// 一般将 setxxx => updatexx
const [person, updatePerson] = useImmer(personList)

// recipe:用于操作draftState的函数
updatePerson(draft => {
  draft.artwork.image.alt = 'test1';
});

三、数组

在 JavaScript 中,数组只是另一种对象,应该将处于 React 状态的数组视为只读,
每次您想更新一个数组时,您都需要将一个新数组传递给您的状态设置函数。

Immer编写简洁的更新state逻辑_第3张图片

使用immer: 可以使用两列的方法

const [artists, setArtists] = useState([]);

举例

 setArtists( 
  [ 
    ...artists, 
    { id: '1', name: name } 
  ]
);

或者

 updateMyList(draft => {
  //push/unshift
  draft.push({ id: '1', name: name })
});

你可能感兴趣的:(javascript,immer)