• 简体中文
  • 更新状态

    扁平更新

    在 Zustand 中更新状态非常简单!用新状态调用提供的 set 函数,它就会与 store 中的现有状态进行浅合并。注意:嵌套状态请参见下一节。

    import { create } from 'zustand'
    
    type State = {
      firstName: string
      lastName: string
    }
    
    type Action = {
      updateFirstName: (firstName: State['firstName']) => void
      updateLastName: (lastName: State['lastName']) => void
    }
    
    // Create your store, which includes both state and (optionally) actions
    const usePersonStore = create<State & Action>()((set) => ({
      firstName: '',
      lastName: '',
      updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
      updateLastName: (lastName) => set(() => ({ lastName: lastName })),
    }))
    
    // In consuming app
    function App() {
      // "select" the needed state and actions, in this case, the firstName value
      // and the action updateFirstName
      const firstName = usePersonStore((state) => state.firstName)
      const updateFirstName = usePersonStore((state) => state.updateFirstName)
    
      return (
        <main>
          <label>
            First name
            <input
              // Update the "firstName" state
              onChange={(e) => updateFirstName(e.currentTarget.value)}
              value={firstName}
            />
          </label>
    
          <p>
            Hello, <strong>{firstName}!</strong>
          </p>
        </main>
      )
    }

    深层嵌套对象

    如果你有一个像下面这样层级很深的状态对象:

    type State = {
      deep: {
        nested: {
          obj: { count: number }
        }
      }
    }

    更新嵌套状态需要费一些功夫,才能确保整个过程以不可变的方式完成。

    常规做法

    与 React 或 Redux 类似,常规做法是逐层复制状态对象。这要借助展开运算符 ...,并手动把新状态值合并进去。如下所示:

      normalInc: () =>
        set((state) => ({
          deep: {
            ...state.deep,
            nested: {
              ...state.deep.nested,
              obj: {
                ...state.deep.nested.obj,
                count: state.deep.nested.obj.count + 1
              }
            }
          }
        })),

    这也太长了!下面我们来看看几个能让日子好过一些的替代方案。

    使用 Immer

    很多人用 Immer 来更新嵌套值。只要需要更新嵌套状态,就可以使用 Immer,比如在 React、Redux 中,当然还有 Zustand!

    对于层级很深的嵌套对象,你可以用 Immer 来精简状态更新。来看一个例子:

      immerInc: () =>
        set(produce((state: State) => { ++state.deep.nested.obj.count })),

    这可真是大幅精简!请注意这里列出的注意事项。

    使用 optics-ts

    还有另一个选择:optics-ts:

      opticsInc: () =>
        set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),

    与 Immer 不同,optics-ts 不使用代理,也不使用可变(mutation)语法。

    使用 Ramda

    你也可以使用 Ramda:

      ramdaInc: () =>
        set(R.modifyPath(["deep", "nested", "obj", "count"], (c) => c + 1)),

    Ramda 和 optics-ts 也都支持类型。

    演示

    https://stackblitz.com/edit/vitejs-vite-j6bjdygu