迁移到 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'
变更
迁移
改用 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>()。
适用的导入
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) => ..., ...)。