主题
迁移到 v4
¥Migrating to v4
唯一的重大变化是在类型中。如果你将 Zusand 与 TypeScript 或 JSDoc 类型注释一起使用,则本指南适用。否则,无需迁移。
¥The only breaking changes are in types. If you are using Zustand with TypeScript or JSDoc type annotations, this guide applies. Otherwise, no migration is required.
此外,建议先阅读新的 TypeScript 指南,以便更容易理解迁移。
¥Also, it's recommended to first read the new TypeScript Guide, so that the migration is easier to understand.
除了本迁移指南之外,你还可以检查 Zusand 存储库中从 v3 到 v4 的测试文件的 diff。
¥In addition to this migration guide, you can also check the diff of the test files in the Zustand repository from v3 to v4.
create
适用的导入
¥Applicable imports
ts
import create from 'zustand'
import create from 'zustand/vanilla'
更改
¥Change
diff
- create:
- < State
- , StoreSetState = StoreApi<State>["set"]
- , StoreGetState = StoreApi<State>["get"]
- , Store = StoreApi<State>
- >
- (f: ...) => ...
+ create:
+ { <State>(): (f: ...) => ...
+ , <State, Mutators>(f: ...) => ...
+ }
迁移
¥Migration
如果你没有将任何类型参数传递给 create
,则无需迁移。
¥If you are not passing any type parameters to create
, no migration is required.
如果你使用的是 "leaf" 中间件(如 combine
或 redux
),请从 create
中删除所有类型参数。
¥If you are using a "leaf" middleware like combine
or redux
, remove all type parameters from create
.
否则,将 create<T, ...>(...)
替换为 create<T>()(...)
。
¥Else, replace create<T, ...>(...)
with create<T>()(...)
.
StateCreator
适用的导入
¥Applicable imports
ts
import type { StateCreator } from 'zustand'
import type { StateCreator } from 'zustand/vanilla'
更改
¥Change
diff
- 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
+ > =
+ ...
迁移
¥Migration
如果你使用的是 StateCreator
,则你可能正在编写中间件或使用 "slices" 模式。为此,请查看 TypeScript 指南的 编写中间件和高级用法 和 常见秘诀 部分。
¥If you are using StateCreator
, you are likely authoring a middleware or using the "slices" pattern. For that check the Authoring middlewares and advanced usage and Common recipes sections of the TypeScript Guide.
PartialState
适用的导入
¥Applicable imports
ts
import type { PartialState } from 'zustand'
import type { PartialState } from 'zustand/vanilla'
更改
¥Change
diff
- 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>)
迁移
¥Migration
将 PartialState<T, ...>
替换为 PartialState<T>
,最好在 tsconfig.json
中打开 exactOptionalPropertyTypes
:
¥Replace PartialState<T, ...>
with PartialState<T>
and preferably turn on exactOptionalPropertyTypes
in your tsconfig.json
:
json
{
"compilerOptions": {
"exactOptionalPropertyTypes": true
}
}
我们不再使用技巧来禁止将 { foo: undefined }
分配给 Partial<{ foo: string }>
。相反,我们依靠用户打开 exactOptionalPropertyTypes
。
¥We're no longer using the trick to disallow { foo: undefined }
to be assigned to Partial<{ foo: string }>
. Instead, we're relying on the users to turn on exactOptionalPropertyTypes
.
useStore
适用的导入
¥Applicable imports
ts
import { useStore } from 'zustand'
import { useStore } from 'zustand/react'
更改
¥Change
diff
- 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
迁移
¥Migration
如果你没有将任何类型参数传递给 useStore
,则无需迁移。
¥If you are not passing any type parameters to useStore
, no migration is required.
如果是这样,建议删除所有类型参数,或者将 store 类型而不是状态类型作为第一个参数传递。
¥If you are, it's recommended to remove all the type parameters, or pass the store type instead of the state type as the first parameter.
UseBoundStore
适用的导入
¥Applicable imports
ts
import type { UseBoundStore } from 'zustand'
import type { UseBoundStore } from 'zustand/react'
更改
¥Change
diff
- 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
迁移
¥Migration
将 UseBoundStore<T>
替换为 UseBoundStore<StoreApi<T>>
,将 UseBoundStore<T, S>
替换为 UseBoundStore<S>
¥Replace UseBoundStore<T>
with UseBoundStore<StoreApi<T>>
, and UseBoundStore<T, S>
with UseBoundStore<S>
UseContextStore
适用的导入
¥Applicable imports
ts
import type { UseContextStore } from 'zustand/context'
更改
¥Change
diff
- type UseContextStore
迁移
¥Migration
改用 typeof MyContext.useStore
¥Use typeof MyContext.useStore
instead
createContext
适用的导入
¥Applicable imports
ts
import createContext from 'zustand/context'
更改
¥Change
diff
createContext:
- <State, Store = StoreApi<State>>() => ...
+ <Store>() => ...
迁移
¥Migration
将 createContext<T>()
替换为 createContext<StoreApi<T>>()
,将 createContext<T, S>()
替换为 createContext<S>()
。
¥Replace createContext<T>()
with createContext<StoreApi<T>>()
, and createContext<T, S>()
with createContext<S>()
.
combine
, devtools
, subscribeWithSelector
适用的导入
¥Applicable imports
ts
import { combine } from 'zustand/middleware'
import { devtools } from 'zustand/middleware'
import { subscribeWithSelector } from 'zustand/middleware'
更改
¥Change
diff
- combine:
- <T, U>(...) => ...
+ combine:
+ <T, U, Mps, Mcs>(...) => ...
- devtools:
- <T>(...) => ...
+ devtools:
+ <T, Mps, Mcs>(...) => ...
- subscribeWithSelector:
- <T>(...) => ...
+ subscribeWithSelector:
+ <T, Mps, Mcs>(...) => ...
迁移
¥Migration
如果你没有将任何类型参数传递给 combine
、devtools
或 subscribeWithSelector
,则无需迁移。
¥If you are not passing any type parameters to combine
, devtools
, or subscribeWithSelector
, no migration is required.
如果是这样,请删除所有类型参数,因为它们是自动推断的。
¥If you are, remove all the type parameters, as they are inferred automatically.
persist
适用的导入
¥Applicable imports
ts
import { persist } from 'zustand/middleware'
更改
¥Change
diff
- persist:
- <T, U = Partial<T>>(...) => ...
+ persist:
+ <T, Mps, Mcs, U = T>(...) => ...
迁移
¥Migration
如果你传递了任何类型参数,请将其删除,因为它们是自动推断的。
¥If you are passing any type parameters, remove them as they are inferred automatically.
接下来,如果你传递了 partialize
选项,则无需执行迁移的进一步步骤。
¥Next, if you are passing the partialize
option, there is no further steps required for migration.
如果你没有传递 partialize
选项,你可能会看到一些编译错误。如果你没有看到任何函数,则无需进一步迁移。
¥If you are not passing the partialize
option, you might see some compilation errors. If you do not see any, there is no further migration required.
部分状态的类型现在是 T
而不是 Partial<T>
,这与默认 partialize
(即身份(s => s
))的运行时行为一致。
¥The type of partialized state is now T
instead of Partial<T>
, which aligns with the runtime behavior of the default partialize
, which is an identity (s => s
).
如果你看到一些编译错误,你必须自己查找并修复错误,因为它们可能表明代码不健全。或者,解决方法是将 s => s as Partial<typeof s>
传递给 partialize
。如果你的部分状态确实是 Partial<T>
,则不应该遇到任何错误。
¥If you see some compilation errors, you have to find and fix the errors yourself, because they might be indicative of unsound code. Alternatively, the workaround will be passing s => s as Partial<typeof s>
to partialize
. If your partialized state is truly Partial<T>
, you should not encounter any bugs.
运行时行为没有改变,只有类型现在正确了。
¥The runtime behavior has not changed, only the types are now correct.
redux
适用的导入
¥Applicable imports
ts
import { redux } from 'zustand/middleware'
更改
¥Change
diff
- redux:
- <T, A>(...) => ...
+ redux:
+ <T, A, Mps, Mcs>(...) => ...
迁移
¥Migration
如果你没有将任何类型参数传递给 redux
,则无需迁移。
¥If you are not passing any type parameters to redux
, no migration is required.
如果是这样,请删除所有类型参数,并仅注释第二个(操作)参数。也就是说,用 redux((state, action: A) => ..., ...)
替换 redux<T, A>((state, action) => ..., ...)
。
¥If you are, remove all the type parameters, and annotate only the second (action) parameter. That is, replace redux<T, A>((state, action) => ..., ...)
with redux((state, action: A) => ..., ...)
.