• 简体中文
  • 迁移到 v4

    唯一的破坏性变更都在类型层面。 如果你在配合 TypeScript 或 JSDoc 类型注解使用 Zustand, 本指南适用于你。 否则无需任何迁移。

    另外,建议先阅读新的 TypeScript 进阶指南, 这样更容易理解本次迁移。

    除了这篇迁移指南,你还可以查看 Zustand 仓库中测试文件从 v3 到 v4 的 diff。

    create

    适用的导入

    import create from 'zustand'
    import create from 'zustand/vanilla'

    变更

    - create:
    -   < State
    -   , StoreSetState = StoreApi<State>["set"]
    -   , StoreGetState = StoreApi<State>["get"]
    -   , Store = StoreApi<State>
    -   >
    -     (f: ...) => ...
    + create:
    +   { <State>(): (f: ...) => ...
    +   , <State, Mutators>(f: ...) => ...
    +   }

    迁移

    如果你没有向 create 传递任何类型参数, 则无需迁移。

    如果你使用了 combine 或 redux 这类“叶子”中间件, 请从 create 中移除所有类型参数。

    否则,把 create<T, ...>(...) 替换为 create<T>()(...)。

    StateCreator

    适用的导入

    import type { StateCreator } from 'zustand'
    import type { StateCreator } from 'zustand/vanilla'

    变更

    - type StateCreator
    -   < State
    -   , StoreSetState = StoreApi<State>["set"]
    -   , StoreGetState = StoreApi<State>["get"]
    -   , Store = StoreApi<State>
    -   > =
    -     ...
    + type StateCreator
    +   < State
    +   , InMutators extends [StoreMutatorIdentifier, unknown][] = []
    +   , OutMutators extends [StoreMutatorIdentifier, unknown][] = []
    +   , Return = State
    +   > =
    +     ...

    迁移

    如果你在使用 StateCreator, 多半是在编写中间件, 或者在使用“切片”模式。 这两种情况请参阅 TypeScript 进阶指南的 编写中间件与高级用法 和 常见做法 两节。

    PartialState

    适用的导入

    import type { PartialState } from 'zustand'
    import type { PartialState } from 'zustand/vanilla'

    变更

    - type PartialState
    -   < T extends State
    -   , K1 extends keyof T = keyof T
    -   , K2 extends keyof T = K1
    -   , K3 extends keyof T = K2
    -   , K4 extends keyof T = K3
    -   > =
    -   | (Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
    -   | ((state: T) => Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
    + type PartialState<T> =
    +   | Partial<T>
    +   | ((state: T) => Partial<T>)

    迁移

    把 PartialState<T, ...> 替换为 PartialState<T>, 并建议在 tsconfig.json 中开启 exactOptionalPropertyTypes:

    {
      "compilerOptions": {
        "exactOptionalPropertyTypes": true
      }
    }

    我们不再使用那套小技巧来禁止把 { foo: undefined } 赋值给 Partial<{ foo: string }>。 现在改为依赖用户自行开启 exactOptionalPropertyTypes。

    useStore

    适用的导入

    import { useStore } from 'zustand'
    import { useStore } from 'zustand/react'

    变更

    - useStore:
    -   { <State>(store: StoreApi<State>): State
    -   , <State, StateSlice>
    -       ( store: StoreApi<State>
    -       , selector: StateSelector<State, StateSlice>,
    -       , equals?: EqualityChecker<StateSlice>
    -       ): StateSlice
    -   }
    + useStore:
    +   <Store, StateSlice = ExtractState<Store>>
    +     ( store: Store
    +     , selector?: StateSelector<State, StateSlice>,
    +     , equals?: EqualityChecker<StateSlice>
    +     )
    +       => StateSlice

    迁移

    如果你没有向 useStore 传递任何类型参数, 则无需迁移。

    如果你传了,建议移除所有类型参数, 或者把第一个参数改为 store 类型,而不是 state 类型。

    UseBoundStore

    适用的导入

    import type { UseBoundStore } from 'zustand'
    import type { UseBoundStore } from 'zustand/react'

    变更

    - type UseBoundStore<
    -   State,
    -   Store = StoreApi<State>
    - > =
    -   & { (): T
    -     , <StateSlice>
    -         ( selector: StateSelector<State, StateSlice>
    -         , equals?: EqualityChecker<StateSlice>
    -         ): U
    -     }
    -   & Store
    + type UseBoundStore<Store> =
    +   & (<StateSlice = ExtractState<S>>
    +       ( selector?: (state: ExtractState<S>) => StateSlice
    +       , equals?: (a: StateSlice, b: StateSlice) => boolean
    +       ) => StateSlice
    +     )
    +   & S

    迁移

    把 UseBoundStore<T> 替换为 UseBoundStore<StoreApi<T>>, 把 UseBoundStore<T, S> 替换为 UseBoundStore<S>

    UseContextStore

    适用的导入

    import type { UseContextStore } from 'zustand/context'

    变更

    - type UseContextStore

    迁移

    改用 typeof MyContext.useStore

    createContext

    适用的导入

    import createContext from 'zustand/context'

    变更

      createContext:
    -   <State, Store = StoreApi<State>>() => ...
    +   <Store>() => ...

    迁移

    把 createContext<T>() 替换为 createContext<StoreApi<T>>(), 把 createContext<T, S>() 替换为 createContext<S>()。

    combine、devtools、subscribeWithSelector

    适用的导入

    import { combine } from 'zustand/middleware'
    import { devtools } from 'zustand/middleware'
    import { subscribeWithSelector } from 'zustand/middleware'

    变更

    - combine:
    -   <T, U>(...) => ...
    + combine:
    +   <T, U, Mps, Mcs>(...) => ...
    
    - devtools:
    -   <T>(...) => ...
    + devtools:
    +   <T, Mps, Mcs>(...) => ...
    
    - subscribeWithSelector:
    -   <T>(...) => ...
    + subscribeWithSelector:
    +   <T, Mps, Mcs>(...) => ...

    迁移

    如果你没有向 combine、devtools 或 subscribeWithSelector 传递任何类型参数, 则无需迁移。

    如果你传了, 请移除所有类型参数, 因为它们会被自动推断。

    persist

    适用的导入

    import { persist } from 'zustand/middleware'

    变更

    - persist:
    -   <T, U = Partial<T>>(...) => ...
    + persist:
    +   <T, Mps, Mcs, U = T>(...) => ...

    迁移

    如果你传入了任何类型参数, 请移除它们,因为它们会被自动推断。

    接下来,如果你传入了 partialize 选项, 则迁移无需再做其他事情。

    如果你没有传入 partialize 选项, 可能会看到一些编译错误。 如果没有看到任何错误, 就不需要进一步迁移。

    经 partialize 处理后的状态,其类型现在是 T,而不是 Partial<T>, 这与默认 partialize 的运行时行为一致, 因为默认 partialize 是一个恒等函数(s => s)。

    如果看到编译错误, 你需要自己找出并修复这些错误, 因为它们可能暗示代码本身存在不可靠之处。 另一种变通方案是向 partialize 传入 s => s as Partial<typeof s>。 如果你经 partialize 处理后的状态确实就是 Partial<T>, 那你不应该遇到任何 bug。

    运行时行为没有改变, 只是类型现在正确了。

    redux

    适用的导入

    import { redux } from 'zustand/middleware'

    变更

    - redux:
    -   <T, A>(...) => ...
    + redux:
    +   <T, A, Mps, Mcs>(...) => ...

    迁移

    如果你没有向 redux 传递任何类型参数, 则无需迁移。

    如果你传了, 请移除所有类型参数, 只给第二个参数(action)标注类型。 也就是说,把 redux<T, A>((state, action) => ..., ...) 替换为 redux((state, action: A) => ..., ...)。