• 简体中文
  • immer

    immer 中间件让你可以执行不可变更新。

    WARNING

    要在 zustand/middleware/immer 中使用 immer,你需要先安装 immer 库。

    const nextStateCreatorFn = immer(stateCreatorFn)

    类型

    签名

    immer<T>(stateCreatorFn: StateCreator<T, [], []>): StateCreator<T, [['zustand/immer', never]], []>

    Mutator

    ;['zustand/immer', never]

    参考

    immer(stateCreatorFn)

    参数

    • stateCreatorFn:一个接收 set 函数、get 函数和 store 作为参数的函数。 通常,你会返回一个对象,其中包含你希望暴露的方法。

    返回值

    immer 返回一个状态创建函数。

    用法

    无需样板代码即可更新状态

    在下面的示例中,我们要更新 person 对象。由于它是一个嵌套对象,我们需要在更新前复制整个对象。

    import { createStore } from 'zustand/vanilla'
    
    type PersonStoreState = {
      person: { firstName: string; lastName: string; email: string }
    }
    
    type PersonStoreActions = {
      setPerson: (
        nextPerson:
          | PersonStoreState['person']
          | ((person: PersonStoreState['person']) => PersonStoreState['person']),
      ) => void
    }
    
    type PersonStore = PersonStoreState & PersonStoreActions
    
    const personStore = createStore<PersonStore>()((set) => ({
      person: {
        firstName: 'Barbara',
        lastName: 'Hepworth',
        email: '[email protected]',
      },
      setPerson: (nextPerson) =>
        set((state) => ({
          person: typeof nextPerson === 'function' ? nextPerson(state.person) : nextPerson,
        })),
    }))
    
    const $firstNameInput = document.getElementById('first-name') as HTMLInputElement
    const $lastNameInput = document.getElementById('last-name') as HTMLInputElement
    const $emailInput = document.getElementById('email') as HTMLInputElement
    const $result = document.getElementById('result') as HTMLDivElement
    
    function handleFirstNameChange(event: Event) {
      personStore.getState().setPerson((person) => ({
        ...person,
        firstName: (event.target as any).value,
      }))
    }
    
    function handleLastNameChange(event: Event) {
      personStore.getState().setPerson((person) => ({
        ...person,
        lastName: (event.target as any).value,
      }))
    }
    
    function handleEmailChange(event: Event) {
      personStore.getState().setPerson((person) => ({
        ...person,
        email: (event.target as any).value,
      }))
    }
    
    $firstNameInput.addEventListener('input', handleFirstNameChange)
    $lastNameInput.addEventListener('input', handleLastNameChange)
    $emailInput.addEventListener('input', handleEmailChange)
    
    const render: Parameters<typeof personStore.subscribe>[0] = (state) => {
      $firstNameInput.value = state.person.firstName
      $lastNameInput.value = state.person.lastName
      $emailInput.value = state.person.email
    
      $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`
    }
    
    render(personStore.getInitialState(), personStore.getInitialState())
    
    personStore.subscribe(render)

    下面是 html 代码

    <label style="display: block">
      First name:
      <input id="first-name" />
    </label>
    <label style="display: block">
      Last name:
      <input id="last-name" />
    </label>
    <label style="display: block">
      Email:
      <input id="email" />
    </label>
    <p id="result"></p>

    为了避免在更新前手动复制整个对象,我们将使用 immer 中间件。

    import { createStore } from 'zustand/vanilla'
    import { immer } from 'zustand/middleware/immer'
    
    type PersonStoreState = {
      person: { firstName: string; lastName: string; email: string }
    }
    
    type PersonStoreActions = {
      setPerson: (
        nextPerson:
          | PersonStoreState['person']
          | ((person: PersonStoreState['person']) => PersonStoreState['person']),
      ) => void
    }
    
    type PersonStore = PersonStoreState & PersonStoreActions
    
    const personStore = createStore<PersonStore>()(
      immer((set) => ({
        person: {
          firstName: 'Barbara',
          lastName: 'Hepworth',
          email: '[email protected]',
        },
        setPerson: (nextPerson) =>
          set((state) => {
            state.person = typeof nextPerson === 'function' ? nextPerson(state.person) : nextPerson
          }),
      })),
    )
    
    const $firstNameInput = document.getElementById('first-name') as HTMLInputElement
    const $lastNameInput = document.getElementById('last-name') as HTMLInputElement
    const $emailInput = document.getElementById('email') as HTMLInputElement
    const $result = document.getElementById('result') as HTMLDivElement
    
    function handleFirstNameChange(event: Event) {
      personStore.getState().setPerson((person) => {
        person.firstName = (event.target as any).value
      })
    }
    
    function handleLastNameChange(event: Event) {
      personStore.getState().setPerson((person) => {
        person.lastName = (event.target as any).value
      })
    }
    
    function handleEmailChange(event: Event) {
      personStore.getState().setPerson((person) => {
        person.email = (event.target as any).value
      })
    }
    
    $firstNameInput.addEventListener('input', handleFirstNameChange)
    $lastNameInput.addEventListener('input', handleLastNameChange)
    $emailInput.addEventListener('input', handleEmailChange)
    
    const render: Parameters<typeof personStore.subscribe>[0] = (state) => {
      $firstNameInput.value = state.person.firstName
      $lastNameInput.value = state.person.lastName
      $emailInput.value = state.person.email
    
      $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`
    }
    
    render(personStore.getInitialState(), personStore.getInitialState())
    
    personStore.subscribe(render)

    疑难解答

    TBD