Skip to content

更新状态

¥Updating state

扁平更新

¥Flat updates

使用 Zustand 更新状态很简单!使用新状态调用提供的 set 函数,它将与存储中的现有状态进行浅层合并。注意 请参阅下一节了解嵌套状态。

¥Updating state with Zustand is simple! Call the provided set function with the new state, and it will be shallowly merged with the existing state in the store. Note See next section for nested state.

tsx
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>
  )
}

深度嵌套对象

¥Deeply nested object

如果你有这样的深度状态对象:

¥If you have a deep state object like this:

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

更新嵌套状态需要付出一些努力才能确保该过程不可变地完成。

¥Updating nested state requires some effort to ensure the process is completed immutably.

正常方法

¥Normal approach

与 React 或 Redux 类似,正常方法是复制状态对象的每一级。这是通过扩展运算符 ... 完成的,并通过手动将其与新状态值合并。像这样:

¥Similar to React or Redux, the normal approach is to copy each level of the state object. This is done with the spread operator ..., and by manually merging that in with the new state values. Like so:

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

这很长!让我们探索一些可以让你的生活更轻松的替代方案。

¥This is very long! Let's explore some alternatives that will make your life easier.

使用 Immer

¥With Immer

许多人使用 Immer 来更新嵌套值。你可以在任何需要更新嵌套状态时使用 Immer,例如在 React、Redux 以及 Zustand 中!

¥Many people use Immer to update nested values. Immer can be used anytime you need to update nested state such as in React, Redux and of course, Zustand!

你可以使用 Immer 缩短深度嵌套对象的状态更新。让我们看一个例子:

¥You can use Immer to shorten your state updates for deeply nested object. Let's take a look at an example:

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

多么大的减少!请注意 此处列出的陷阱

¥What a reduction! Please take note of the gotchas listed here.

使用 optics-ts

¥With optics-ts

optics-ts 还有另一种选择:

¥There is another option with optics-ts:

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

与 Immer 不同,optics-ts 不使用代理或修改语法。

¥Unlike Immer, optics-ts doesn't use proxies or mutation syntax.

使用 Ramda

¥With Ramda

你还可以使用 Ramda

¥You can also use Ramda:

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

ramda 和 optics-ts 也适用于类型。

¥Both ramda and optics-ts also work with types.

CodeSandbox 演示

¥CodeSandbox Demo

https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx

Zustand v5.0 中文网 - 粤ICP备13048890号