zustand-computed 中文文档教程
zustand-compulated
zustand-compulated 是一个轻量级、TypeScript 友好的状态管理系统中间件 Zustand 。这是一个简单的层,在商店中发生任何状态更改后添加转换函数。
安装
yarn add zustand-computed
用法
中间件层接受您的存储创建函数和计算函数,该函数将您的状态转换为计算状态。它不需要处理合并状态。
import computed from "zustand-computed"
const computeState = (state) => ({
countSq: state.count ** 2,
})
const useStore = create(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState
)
)
对于类型,前面的示例将如下所示:
import computed from "zustand-computed"
type Store = {
count: number
inc: () => void
dec: () => void
}
type ComputedStore = {
countSq: number
}
const computeState = (state: Store): ComputedStore => ({
countSq: state.count ** 2,
})
// use curried create
const useStore = create<Store>()(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState
)
)
然后可以在 React 组件中或通过 Zustand API 正常使用存储。
function Counter() {
const { count, countSq, inc, dec } = useStore()
return (
<div>
<span>{count}</span>
<br />
<span>{countSq}</span>
<br />
<button onClick={inc}>+1</button>
<button onClick={dec}>-1</button>
</div>
)
}
可以在“example”目录下找到功能齐全的示例。
使用中间件
这是一个使用中间件的示例。按预期工作。
const useStore = create<Store>()(
devtools(
computed(
immer((set) => ({
count: 1,
inc: () =>
set((state) => {
// example with Immer middleware
state.count += 1
}),
dec: () => set((state) => ({ count: state.count - 1 })),
})),
computeState
)
)
)
选择器
默认情况下,当 zustand-compulated
运行 computeState
函数时,它会跟踪访问的变量,并且如果这些变量之一没有更改,则不会触发计算。如果您在computeState内部嵌套了控制流,或者您希望它在所有更改上运行,而不管在computeState
内部使用,这可能会出现问题代码>.要禁用自动选择器检测,您可以将第三个 opts
变量传递给 compated
构造函数,例如
const useStore = create<Store, [["chrisvander/zustand-computed", ComputedStore]]>(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState,
{ disableProxy: true }
)
)
其他选项包括传递 keys
数组,该数组明确拼写出触发重新计算的选择器。您还可以传递自定义 equalityFn
,例如 fast-deep-equal 而不是默认的 zustand/shallow 。
zustand-computed
zustand-computed is a lightweight, TypeScript-friendly middleware for the state management system Zustand. It's a simple layer which adds a transformation function after any state change in your store.
Install
yarn add zustand-computed
Usage
The middleware layer takes in your store creation function and a compute function, which transforms your state into a computed state. It does not need to handle merging states.
import computed from "zustand-computed"
const computeState = (state) => ({
countSq: state.count ** 2,
})
const useStore = create(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState
)
)
With types, the previous example would look like this:
import computed from "zustand-computed"
type Store = {
count: number
inc: () => void
dec: () => void
}
type ComputedStore = {
countSq: number
}
const computeState = (state: Store): ComputedStore => ({
countSq: state.count ** 2,
})
// use curried create
const useStore = create<Store>()(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState
)
)
The store can then be used as normal in a React component or via the Zustand API.
function Counter() {
const { count, countSq, inc, dec } = useStore()
return (
<div>
<span>{count}</span>
<br />
<span>{countSq}</span>
<br />
<button onClick={inc}>+1</button>
<button onClick={dec}>-1</button>
</div>
)
}
A fully-featured example can be found under the "example" directory.
With Middleware
Here's an example with middleware. Works as expected.
const useStore = create<Store>()(
devtools(
computed(
immer((set) => ({
count: 1,
inc: () =>
set((state) => {
// example with Immer middleware
state.count += 1
}),
dec: () => set((state) => ({ count: state.count - 1 })),
})),
computeState
)
)
)
Selectors
By default, when zustand-computed
runs your computeState
function, it tracks accessed variables and does not trigger a computation if one of those variables do not change. This could potentially be problematic if you have nested control flow inside of computeState
, or perhaps you want it to run on all changes regardless of use inside of computeState
. To disable automatic selector detection, you can pass a third opts
variable to the computed
constructor, e.g.
const useStore = create<Store, [["chrisvander/zustand-computed", ComputedStore]]>(
computed(
(set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}),
computeState,
{ disableProxy: true }
)
)
Other options include passing a keys
array, which explicitly spell out the selectors which trigger re-computation. You can also pass a custom equalityFn
, such as fast-deep-equal instead of the default zustand/shallow
.