基本使用
使用TypeScript的区别在于,你需要写成 create()(…) (注意额外的括号以及类型参数),而不是 create(…)。其中 T 是用来注解状态的类型。例如:
另外,你也可以使用 combine,它可以推断出状态,这样你就不需要去类型化它。
请注意,我们在使用 combine 时不使用柯里化版本,因为 combine “创建”了状态。当使用创建状态的中间件时,不需要使用柯里化版本,因为现在可以推断出状态。创建状态的另一个中间件是 redux。所以当使用 combine、redux 或任何其他创建状态的自定义中间件时,我们不推荐使用柯里化版本。
使用中间件
在TypeScript中使用中间件不需要做任何特殊的事情。
只需要确保你是在 create 里面直接使用它们,以使上下文推断起作用。做一些甚至稍微复杂的事情,如下面的 myMiddlewares 会需要更高级的类型。
此外,我们建议尽可能地将 devtools 中间件放在最后。例如,当你将它与 immer 作为中间件一起使用时,它应该是 devtools(immer(…)) 而不是 immer(devtools(…))。这是因为 devtools 改变了 setState 并在其上添加了一个类型参数,如果其他中间件(如 immer)在 devtools 之前也改变了 setState,这个类型参数可能会丢失。因此,将 devtools 放在最后可以确保没有中间件在它之前改变 setState。
编写中间件和高级使用
假设你需要编写这个假设性的中间件。
Zustand 中间件可以改变存储。但我们如何在类型级别编码这种变化呢?也就是说,我们如何类型化 foo 使得这段代码可以编译?
对于一个通常的静态类型语言,这是不可能的。但是,由于 TypeScript,Zustand 有一个叫做 “高阶变异器” 的东西,使得这成为可能。如果你正在处理复杂的类型问题,比如类型化一个中间件或使用 StateCreator 类型,你将需要理解这个实现细节。关于这个,你可以查看 #710。
如果你急于知道这个特定问题的答案,你可以在这里看到。
常见的配方
不改变存储类型的中间件
改变存储类型的中间件
不使用柯里化的 create
推荐的使用 create 的方式是使用柯里化的解决方案,像这样:create()(…)。这是因为它使你能够推断出存储类型。但是,如果出于某种原因你不想使用这个解决方案,你可以像下面这样传递类型参数。注意,在某些情况下,这作为一个断言而不是注解,所以我们不推荐它。
切片模式
关于切片模式的详细解释可以在这里找到。
如果你有一些中间件,那么将 StateCreator<MyState, [], [], MySlice> 替换为 StateCreator<MyState, Mutators, [], MySlice>。例如,如果你正在使用 devtools,那么它将是 StateCreator<MyState, [[“zustand/devtools”, never]], [], MySlice>。查看 “Middlewares and their mutators reference” 部分以获取所有变异器的列表。
为 vanilla stores 绑定 useStore 钩子
如果你需要经常创建绑定的 useStore 钩子并希望保持 DRY,你也可以创建一个抽象的 createBoundedUseStore 函数…
中间件及其变换器参考
devtools — [“zustand/devtools”, never]
persist — [“zustand/persist”, YourPersistedState]
YourPersistedState 是您要持久保存的状态类型,即 options.partialize 的返回类型,如果您没有传递 partialize 选项,YourPersistedState 将变为 Partial。有时传递实际的 PersistedState 不起作用。在这种情况下,请尝试传递 unknown。
immer — [“zustand/immer”, never]
subscribeWithSelector — [“zustand/subscribeWithSelector”, never]
redux — [“zustand/redux”, YourAction]
combine — 由于 combine 不会改变存储,因此没有变换器。