• 简体中文
  • Immer 中间件

    Immer 中间件让你能够以更方便的方式使用不可变状态。此外,借助 Immer,你还可以简化 Zustand 中不可变数据结构的处理。

    安装

    要在 Zustand 中使用 Immer 中间件,你需要把 Immer 安装为直接依赖。

    npm install immer

    用法

    (注意类型参数后面的额外括号,如 TypeScript 进阶指南 中所述。)

    更新简单状态

    import { create } from 'zustand'
    import { immer } from 'zustand/middleware/immer'
    
    type State = {
      count: number
    }
    
    type Actions = {
      increment: (qty: number) => void
      decrement: (qty: number) => void
    }
    
    export const useCountStore = create<State & Actions>()(
      immer((set) => ({
        count: 0,
        increment: (qty: number) =>
          set((state) => {
            state.count += qty
          }),
        decrement: (qty: number) =>
          set((state) => {
            state.count -= qty
          }),
      })),
    )

    更新复杂状态

    import { create } from 'zustand'
    import { immer } from 'zustand/middleware/immer'
    
    interface Todo {
      id: string
      title: string
      done: boolean
    }
    
    type State = {
      todos: Record<string, Todo>
    }
    
    type Actions = {
      toggleTodo: (todoId: string) => void
    }
    
    export const useTodoStore = create<State & Actions>()(
      immer((set) => ({
        todos: {
          '82471c5f-4207-4b1d-abcb-b98547e01a3e': {
            id: '82471c5f-4207-4b1d-abcb-b98547e01a3e',
            title: 'Learn Zustand',
            done: false,
          },
          '354ee16c-bfdd-44d3-afa9-e93679bda367': {
            id: '354ee16c-bfdd-44d3-afa9-e93679bda367',
            title: 'Learn Jotai',
            done: false,
          },
          '771c85c5-46ea-4a11-8fed-36cc2c7be344': {
            id: '771c85c5-46ea-4a11-8fed-36cc2c7be344',
            title: 'Learn Valtio',
            done: false,
          },
          '363a4bac-083f-47f7-a0a2-aeeee153a99c': {
            id: '363a4bac-083f-47f7-a0a2-aeeee153a99c',
            title: 'Learn Signals',
            done: false,
          },
        },
        toggleTodo: (todoId: string) =>
          set((state) => {
            state.todos[todoId].done = !state.todos[todoId].done
          }),
      })),
    )

    注意事项

    本节介绍将 Zustand 与 Immer 一起使用时需要留意的一些问题。

    我的订阅没有被调用

    如果你在使用 Immer,请确保你确实遵循了 Immer 的规则。

    例如,要让类对象正常工作,你必须添加 [immerable] = true。如果不这样做,Immer 仍会修改该对象,但不是通过代理来修改,因此它也会一并更新当前状态。Zustand 会检查状态是否真的发生了变化,因此,当前状态与下一个状态相等时(如果没有正确处理),它就会跳过对订阅的调用。

    演示