主题
TypeScript 指南
¥TypeScript Guide
基本用法
¥Basic usage
使用 TypeScript 时的区别在于,你不必编写 create(...)
,而是必须编写 create<T>()(...)
(请注意类型参数中还有额外的括号 ()
),其中 T
是要注释的状态的类型。例如:
¥The difference when using TypeScript is that instead of writing create(...)
, you have to write create<T>()(...)
(notice the extra parentheses ()
too along with the type parameter) where T
is the type of the state to annotate it. For example:
ts
import { create } from 'zustand'
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
Why can't we simply infer the type from the initial state?
TLDR:因为状态泛型 T
是不变的。
¥TLDR: Because state generic T
is invariant.
考虑这个最小版本 create
:
¥Consider this minimal version create
:
ts
declare const create: <T>(f: (get: () => T) => T) => T
const x = create((get) => ({
foo: 0,
bar: () => get(),
}))
// `x` is inferred as `unknown` instead of
// interface X {
// foo: number,
// bar: () => X
// }
在这里,如果你看看 create
中的 f
类型,即 (get: () => T) => T
,它通过返回 "gives" T
(使其成为协变的),但它也通过 get
"takes" T
(使其成为逆变的)。“那么 T
是从哪里来的?” TypeScript 想知道。这就像先有鸡还是先有蛋的问题。最后,TypeScript 放弃并将 T
推断为 unknown
。
¥Here, if you look at the type of f
in create
, i.e. (get: () => T) => T
, it "gives" T
via return (making it covariant), but it also "takes" T
via get
(making it contravariant). "So where does T
come from?" TypeScript wonders. It's like that chicken or egg problem. At the end TypeScript, gives up and infers T
as unknown
.
因此,只要要推断的泛型是不变的(即协变和逆变),TypeScript 就无法推断它。另一个简单的例子是:
¥So, as long as the generic to be inferred is invariant (i.e. both covariant and contravariant), TypeScript will be unable to infer it. Another simple example would be this:
ts
const createFoo = {} as <T>(f: (t: T) => T) => T
const x = createFoo((_) => 'hello')
在这里,x
再次是 unknown
而不是 string
。
¥Here again, x
is unknown
instead of string
.
More about the inference (just for the people curious and interested in TypeScript)
从某种意义上说,这种推断失败不是问题,因为无法写入类型为 <T>(f: (t: T) => T) => T
的值。也就是说,你无法编写 createFoo
的实际运行时实现。让我们试试看:
¥In some sense this inference failure is not a problem because a value of type <T>(f: (t: T) => T) => T
cannot be written. That is to say you can't write the real runtime implementation of createFoo
. Let's try it:
js
const createFoo = (f) => f(/* ? */)
createFoo
需要返回 f
的返回值。为此,我们首先必须调用 f
。要调用它,我们必须传递 T
类型的值。要传递 T
类型的值,我们首先必须生成它。但当我们甚至不知道 T
是什么时,我们如何生成 T
类型的值?产生 T
类型值的唯一方法是调用 f
,但要调用 f
本身,我们需要一个 T
类型的值。所以你看,实际上不可能写出 createFoo
。
¥createFoo
needs to return the return value of f
. And to do that we first have to call f
. And to call it we have to pass a value of type T
. And to pass a value of type T
we first have to produce it. But how can we produce a value of type T
when we don't even know what T
is? The only way to produce a value of type T
is to call f
, but then to call f
itself we need a value of type T
. So you see it's impossible to actually write createFoo
.
所以我们要说的是,createFoo
情况下的推断失败并不是一个真正的问题,因为不可能实现 createFoo
。但是如果出现 create
,推断失败怎么办?这也不是真正的问题,因为也不可能实现 create
。等一下,如果无法实现 create
,那么 Zusand 如何实现它?答案是,事实并非如此。
¥So what we're saying is, the inference failure in case of createFoo
is not really a problem because it's impossible to implement createFoo
. But what about the inference failure in case of create
? That also is not really a problem because it's impossible to implement create
too. Wait a minute, if it's impossible to implement create
then how does Zustand implement it? The answer is, it doesn't.
Zustand 谎称它实现了 create
的类型,但它只实现了其中的大部分。这是一个通过展示不健全性进行的简单证明。考虑以下代码:
¥Zustand lies that it implemented create
's type, it implemented only the most part of it. Here's a simple proof by showing unsoundness. Consider the following code:
ts
import { create } from 'zustand'
const useBoundStore = create<{ foo: number }>()((_, get) => ({
foo: get().foo,
}))
此代码编译。但是如果我们运行它,我们会得到一个异常:“未捕获的 TypeError:无法读取未定义的属性(读取 'foo')”。这是因为 get
会在创建初始状态之前返回 undefined
(因此在创建初始状态时不应调用 get
)。类型 promise get
永远不会返回 undefined
,但它最初会返回,这意味着 Zusand 未能实现它。
¥This code compiles. But if we run it, we'll get an exception: "Uncaught TypeError: Cannot read properties of undefined (reading 'foo')". This is because get
would return undefined
before the initial state is created (hence you shouldn't call get
when creating the initial state). The types promise that get
will never return undefined
but it does initially, which means Zustand failed to implement it.
当然 Zusand 失败了,因为不可能按照类型 promise 的方式实现 create
(以同样的方式不可能实现 createFoo
)。换句话说,我们没有类型来表达我们已实现的实际 create
。我们不能将 get
输入为 () => T | undefined
,因为这会造成不便,而且它仍然不正确,因为 get
最终确实是 () => T
,只是如果同步调用它将是 () => undefined
。我们需要的是某种 TypeScript 功能,允许我们将 get
键入为 (() => T) & WhenSync<() => undefined>
,这当然是极其牵强的。
¥And of course Zustand failed because it's impossible to implement create
the way types promise (in the same way it's impossible to implement createFoo
). In other words we don't have a type to express the actual create
we have implemented. We can't type get
as () => T | undefined
because it would cause inconvenience and it still won't be correct as get
is indeed () => T
eventually, just if called synchronously it would be () => undefined
. What we need is some kind of TypeScript feature that allows us to type get
as (() => T) & WhenSync<() => undefined>
, which of course is extremely far-fetched.
所以我们有两个问题:缺乏推断和不健全性。如果 TypeScript 可以改进其对不变量的推断,则可以解决推断不足的问题。如果 TypeScript 引入类似 WhenSync
的东西,就可以解决不健全的问题。为了解决缺乏推断的问题,我们手动注释状态类型。我们无法解决不健全的问题,但这不是什么大问题,因为问题不大,无论如何同步调用 get
都没有意义。
¥So we have two problems: lack of inference and unsoundness. Lack of inference can be solved if TypeScript can improve its inference for invariants. And unsoundness can be solved if TypeScript introduces something like WhenSync
. To work around lack of inference we manually annotate the state type. And we can't work around unsoundness, but it's not a big deal because it's not much, calling get
synchronously anyway doesn't make sense.
Why the currying `()(...)`?
TLDR:它是 microsoft/TypeScript#10571 的解决方法。
¥TLDR: It is a workaround for microsoft/TypeScript#10571.
想象一下你有这样的场景:
¥Imagine you have a scenario like this:
ts
declare const withError: <T, E>(
p: Promise<T>,
) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>
declare const doSomething: () => Promise<string>
const main = async () => {
let [error, value] = await withError(doSomething())
}
这里,T
被推断为 string
,E
被推断为 unknown
。你可能希望将 E
注释为 Foo
,因为你确定 doSomething()
会抛出错误的形状。但是,你不能那样做。你可以传递所有泛型或不传递任何泛型。除了将 E
注释为 Foo
之外,你还必须将 T
注释为 string
,即使它无论如何都会被推断出来。解决方案是制作一个在运行时不执行任何操作的 withError
的柯里化版本。其目的只是允许你注释 E
。
¥Here, T
is inferred to be a string
and E
is inferred to be unknown
. You might want to annotate E
as Foo
, because you are certain of the shape of error doSomething()
would throw. However, you can't do that. You can either pass all generics or none. Along with annotating E
as Foo
, you will also have to annotate T
as string
even though it gets inferred anyway. The solution is to make a curried version of withError
that does nothing at runtime. Its purpose is to just allow you annotate E
.
ts
declare const withError: {
<E>(): <T>(
p: Promise<T>,
) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>
<T, E>(
p: Promise<T>,
): Promise<[error: undefined, value: T] | [error: E, value: undefined]>
}
declare const doSomething: () => Promise<string>
interface Foo {
bar: string
}
const main = async () => {
let [error, value] = await withError<Foo>()(doSomething())
}
这样,T
就会被推断出来,你可以注释 E
。当我们想要注释状态(第一个类型参数)但允许推断其他参数时,Zustand 具有相同的用例。
¥This way, T
gets inferred and you get to annotate E
. Zustand has the same use case when we want to annotate the state (the first type parameter) but allow other parameters to get inferred.
或者,你也可以使用 combine
,它可以推断状态,这样你就不需要输入它了。
¥Alternatively, you can also use combine
, which infers the state so that you do not need to type it.
ts
import { create } from 'zustand'
import { combine } from 'zustand/middleware'
const useBearStore = create(
combine({ bears: 0 }, (set) => ({
increase: (by: number) => set((state) => ({ bears: state.bears + by })),
})),
)
Be a little careful
我们通过在你作为参数接收的 set
、get
和 store
类型中稍微撒点谎来实现推断。谎言在于,它们被输入为好像状态是第一个参数,而实际上状态是第一个参数和第二个参数返回的浅合并 ({ ...a, ...b }
)。例如,第二个参数中的 get
具有类型 () => { bears: number }
,这是谎言,因为它应该是 () => { bears: number, increase: (by: number) => void }
。并且 useBearStore
仍然具有正确的类型;例如,useBearStore.getState
的类型为 () => { bears: number, increase: (by: number) => void }
。
¥We achieve the inference by lying a little in the types of set
, get
, and store
that you receive as parameters. The lie is that they're typed as if the state is the first parameter, when in fact the state is the shallow-merge ({ ...a, ...b }
) of both first parameter and the second parameter's return. For example, get
from the second parameter has type () => { bears: number }
and that is a lie as it should be () => { bears: number, increase: (by: number) => void }
. And useBearStore
still has the correct type; for example, useBearStore.getState
is typed as () => { bears: number, increase: (by: number) => void }
.
这并不是谎言,因为 { bears: number }
仍然是 { bears: number, increase: (by: number) => void }
的子类型。因此,在大多数情况下不会有问题。你在使用 replace 时应该小心。例如,set({ bears: 0 }, true)
可以编译但不合理,因为它将删除 increase
函数。另一个需要注意的情况是使用 Object.keys
。Object.keys(get())
将返回 ["bears", "increase"]
而不是 ["bears"]
。get
的返回类型可能会让你犯这些错误。
¥It isn't really a lie because { bears: number }
is still a subtype of { bears: number, increase: (by: number) => void }
. Therefore, there will be no problem in most cases. You should just be careful while using replace. For example, set({ bears: 0 }, true)
would compile but will be unsound as it will delete the increase
function. Another instance where you should be careful is if you use Object.keys
. Object.keys(get())
will return ["bears", "increase"]
and not ["bears"]
. The return type of get
can make you fall for these mistakes.
combine
牺牲了一些类型安全性,以方便不必为状态编写类型。因此,你应该相应地使用 combine
。在大多数情况下都可以,你可以方便地使用它。
¥combine
trades off a little type-safety for the convenience of not having to write a type for state. Hence, you should use combine
accordingly. It is fine in most cases and you can use it conveniently.
请注意,使用 combine
时我们不使用柯里化版本,因为 combine
"creates" 是状态。当使用创建状态的中间件时,没有必要使用柯里化版本,因为现在可以推断出状态。另一个创建状态的中间件是 redux
。因此,在使用 combine
、redux
或任何其他创建状态的自定义中间件时,我们不建议使用柯里化版本。
¥Note that we don't use the curried version when using combine
because combine
"creates" the state. When using a middleware that creates the state, it isn't necessary to use the curried version because the state now can be inferred. Another middleware that creates state is redux
. So when using combine
, redux
, or any other custom middleware that creates the state, we don't recommend using the curried version.
如果你还想在状态声明之外推断状态类型,则可以使用 ExtractState
类型助手:
¥If you want to infer state type also outside of state declaration, you can use the ExtractState
type helper:
ts
import { create, ExtractState } from 'zustand'
import { combine } from 'zustand/middleware'
type BearState = ExtractState<typeof useBearStore>
const useBearStore = create(
combine({ bears: 0 }, (set) => ({
increase: (by: number) => set((state) => ({ bears: state.bears + by })),
})),
)
使用中间件
¥Using middlewares
你无需执行任何特殊操作即可在 TypeScript 中使用中间件。
¥You do not have to do anything special to use middlewares in TypeScript.
ts
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()(
devtools(
persist(
(set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}),
{ name: 'bearStore' },
),
),
)
只需确保你在 create
中立即使用它们,以使上下文推断工作。做一些甚至像下面的 myMiddlewares
这样花哨的事情都需要更高级的类型。
¥Just make sure you are using them immediately inside create
so as to make the contextual inference work. Doing something even remotely fancy like the following myMiddlewares
would require more advanced types.
ts
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
const myMiddlewares = (f) => devtools(persist(f, { name: 'bearStore' }))
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()(
myMiddlewares((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
})),
)
此外,我们建议尽可能最后使用 devtools
中间件。例如,当你将它与 immer
一起使用作为中间件时,它应该是 devtools(immer(...))
而不是 immer(devtools(...))
。这是因为 devtools
改变了 setState
并在其上添加了一个类型参数,如果其他中间件(如 immer
)也在 devtools
之前改变了 setState
,则该参数可能会丢失。因此,在最后使用 devtools
可确保没有中间件在它之前改变 setState
。
¥Also, we recommend using devtools
middleware as last as possible. For example, when you use it with immer
as a middleware, it should be devtools(immer(...))
and not immer(devtools(...))
. This is becausedevtools
mutates the setState
and adds a type parameter on it, which could get lost if other middlewares (like immer
) also mutate setState
before devtools
. Hence using devtools
at the end makes sure that no middlewares mutate setState
before it.
编写中间件和高级用法
¥Authoring middlewares and advanced usage
想象一下你必须编写这个假设的中间件。
¥Imagine you had to write this hypothetical middleware.
ts
import { create } from 'zustand'
const foo = (f, bar) => (set, get, store) => {
store.foo = bar
return f(set, get, store)
}
const useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))
console.log(useBearStore.foo.toUpperCase())
Zustand 中间件可以改变存储。但我们如何在类型级别对突变进行编码?也就是说,我们如何输入 foo
才能使此代码编译?
¥Zustand middlewares can mutate the store. But how could we possibly encode the mutation on the type-level? That is to say how could we type foo
so that this code compiles?
对于通常的静态类型语言,这是不可能的。但是多亏了 TypeScript,Zusand 有一个叫做 "更高级的变量器" 的东西可以实现这一点。如果你正在处理复杂的类型问题,例如键入中间件或使用 StateCreator
类型,则必须了解此实现细节。为此,你可以 查看 #710。
¥For a usual statically typed language, this is impossible. But thanks to TypeScript, Zustand has something called a "higher-kinded mutator" that makes this possible. If you are dealing with complex type problems, like typing a middleware or using the StateCreator
type, you will have to understand this implementation detail. For this, you can check out #710.
如果你急于知道这个特定问题的答案是什么,那么你可以 在此处查看。
¥If you are eager to know what the answer is to this particular problem then you can see it here.
处理动态 replace
标志
¥Handling Dynamic replace
Flag
如果 replace
标志的值在编译时未知并且是动态确定的,你可能会遇到问题。为了解决这个问题,你可以使用一种解决方法,即用 setState
函数的参数注释 replace
参数:
¥If the value of the replace
flag is not known at compile time and is determined dynamically, you might face issues. To handle this, you can use a workaround by annotating the replace
parameter with the parameters of the setState
function:
ts
const replaceFlag = Math.random() > 0.5
const args = [{ bears: 5 }, replaceFlag] as Parameters<
typeof useBearStore.setState
>
store.setState(...args)
使用 as Parameters
解决方法的示例
¥Example with as Parameters
Workaround
ts
import { create } from 'zustand'
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
const replaceFlag = Math.random() > 0.5
const args = [{ bears: 5 }, replaceFlag] as Parameters<
typeof useBearStore.setState
>
useBearStore.setState(...args) // Using the workaround
通过遵循这种方法,你可以确保你的代码处理动态 replace
标志而不会遇到类型问题。
¥By following this approach, you can ensure that your code handles dynamic replace
flags without encountering type issues.
常见秘诀
¥Common recipes
不更改存储类型的中间件
¥Middleware that doesn't change the store type
ts
import { create, StateCreator, StoreMutatorIdentifier } from 'zustand'
type Logger = <
T,
Mps extends [StoreMutatorIdentifier, unknown][] = [],
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
>(
f: StateCreator<T, Mps, Mcs>,
name?: string,
) => StateCreator<T, Mps, Mcs>
type LoggerImpl = <T>(
f: StateCreator<T, [], []>,
name?: string,
) => StateCreator<T, [], []>
const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => {
const loggedSet: typeof set = (...a) => {
set(...(a as Parameters<typeof set>))
console.log(...(name ? [`${name}:`] : []), get())
}
const setState = store.setState
store.setState = (...a) => {
setState(...(a as Parameters<typeof setState>))
console.log(...(name ? [`${name}:`] : []), store.getState())
}
return f(loggedSet, get, store)
}
export const logger = loggerImpl as unknown as Logger
// ---
const useBearStore = create<BearState>()(
logger(
(set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}),
'bear-store',
),
)
更改存储类型的中间件
¥Middleware that changes the store type
ts
import {
create,
StateCreator,
StoreMutatorIdentifier,
Mutate,
StoreApi,
} from 'zustand'
type Foo = <
T,
A,
Mps extends [StoreMutatorIdentifier, unknown][] = [],
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
>(
f: StateCreator<T, [...Mps, ['foo', A]], Mcs>,
bar: A,
) => StateCreator<T, Mps, [['foo', A], ...Mcs]>
declare module 'zustand' {
interface StoreMutators<S, A> {
foo: Write<Cast<S, object>, { foo: A }>
}
}
type FooImpl = <T, A>(
f: StateCreator<T, [], []>,
bar: A,
) => StateCreator<T, [], []>
const fooImpl: FooImpl = (f, bar) => (set, get, _store) => {
type T = ReturnType<typeof f>
type A = typeof bar
const store = _store as Mutate<StoreApi<T>, [['foo', A]]>
store.foo = bar
return f(set, get, _store)
}
export const foo = fooImpl as unknown as Foo
type Write<T extends object, U extends object> = Omit<T, keyof U> & U
type Cast<T, U> = T extends U ? T : U
// ---
const useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))
console.log(useBearStore.foo.toUpperCase())
没有柯里化解决方法的 create
¥create
without curried workaround
使用 create
的推荐方法是使用柯里化解决方法,如下所示:create<T>()(...)
。这是因为它使你能够推断存储类型。但如果出于某种原因你不想使用解决方法,你可以像下面这样传递类型参数。请注意,在某些情况下,这充当断言而不是注释,因此我们不推荐它。
¥The recommended way to use create
is using the curried workaround like so: create<T>()(...)
. This is because it enables you to infer the store type. But if for some reason you do not want to use the workaround, you can pass the type parameters like the following. Note that in some cases, this acts as an assertion instead of annotation, so we don't recommend it.
ts
import { create } from "zustand"
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<
BearState,
[
['zustand/persist', BearState],
['zustand/devtools', never]
]
>(devtools(persist((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}), { name: 'bearStore' }))
切片模式
¥Slices pattern
ts
import { create, StateCreator } from 'zustand'
interface BearSlice {
bears: number
addBear: () => void
eatFish: () => void
}
interface FishSlice {
fishes: number
addFish: () => void
}
interface SharedSlice {
addBoth: () => void
getBoth: () => void
}
const createBearSlice: StateCreator<
BearSlice & FishSlice,
[],
[],
BearSlice
> = (set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
})
const createFishSlice: StateCreator<
BearSlice & FishSlice,
[],
[],
FishSlice
> = (set) => ({
fishes: 0,
addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})
const createSharedSlice: StateCreator<
BearSlice & FishSlice,
[],
[],
SharedSlice
> = (set, get) => ({
addBoth: () => {
// you can reuse previous methods
get().addBear()
get().addFish()
// or do them from scratch
// set((state) => ({ bears: state.bears + 1, fishes: state.fishes + 1 })
},
getBoth: () => get().bears + get().fishes,
})
const useBoundStore = create<BearSlice & FishSlice & SharedSlice>()((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a),
...createSharedSlice(...a),
}))
可以在 此处 中找到有关切片模式的详细说明。
¥A detailed explanation on the slices pattern can be found here.
如果你有一些中间件,则将 StateCreator<MyState, [], [], MySlice>
替换为 StateCreator<MyState, Mutators, [], MySlice>
。例如,如果你使用 devtools
,那么它将是 StateCreator<MyState, [["zustand/devtools", never]], [], MySlice>
。有关所有变量的列表,请参阅 "中间件及其变量参考" 部分。
¥If you have some middlewares then replace StateCreator<MyState, [], [], MySlice>
with StateCreator<MyState, Mutators, [], MySlice>
. For example, if you are using devtools
then it will be StateCreator<MyState, [["zustand/devtools", never]], [], MySlice>
. See the "Middlewares and their mutators reference" section for a list of all mutators.
用于原始存储的有界 useStore
钩子
¥Bounded useStore
hook for vanilla stores
ts
import { useStore } from 'zustand'
import { createStore } from 'zustand/vanilla'
interface BearState {
bears: number
increase: (by: number) => void
}
const bearStore = createStore<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
function useBearStore(): BearState
function useBearStore<T>(selector: (state: BearState) => T): T
function useBearStore<T>(selector?: (state: BearState) => T) {
return useStore(bearStore, selector!)
}
如果你需要经常创建有界 useStore
钩子并希望 DRY 事物,你还可以创建一个抽象的 createBoundedUseStore
函数...
¥You can also make an abstract createBoundedUseStore
function if you need to create bounded useStore
hooks often and want to DRY things up...
ts
import { useStore, StoreApi } from 'zustand'
import { createStore } from 'zustand/vanilla'
interface BearState {
bears: number
increase: (by: number) => void
}
const bearStore = createStore<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
const createBoundedUseStore = ((store) => (selector) =>
useStore(store, selector)) as <S extends StoreApi<unknown>>(
store: S,
) => {
(): ExtractState<S>
<T>(selector: (state: ExtractState<S>) => T): T
}
type ExtractState<S> = S extends { getState: () => infer X } ? X : never
const useBearStore = createBoundedUseStore(bearStore)
中间件及其变量参考
¥Middlewares and their mutators reference
devtools
—["zustand/devtools", never]
persist
—["zustand/persist", YourPersistedState]
YourPersistedState
是你要保留的状态类型,即options.partialize
的返回类型,如果你不传递partialize
选项,则YourPersistedState
变为Partial<YourState>
。此外,sometimes 传递实际的PersistedState
也不起作用。在这些情况下,请尝试传递unknown
。¥
persist
—["zustand/persist", YourPersistedState]
YourPersistedState
is the type of state you are going to persist, ie the return type ofoptions.partialize
, if you're not passingpartialize
options theYourPersistedState
becomesPartial<YourState>
. Also sometimes passing actualPersistedState
won't work. In those cases, try passingunknown
.immer
—["zustand/immer", never]
subscribeWithSelector
—["zustand/subscribeWithSelector", never]
redux
—["zustand/redux", YourAction]
combine
— 没有变量器,因为combine
不会改变存储¥
combine
— no mutator ascombine
does not mutate the store