一种组织 zustand 自定义函数的方法
问题
我们在使用 zustand 的时候,总会遇到需要自定义函数:
// 定义
const useCounter = create((set) => ({
count: 0,
inc: () => {
set((prev) => ({
count: prev.count + 1,
}))
},
}))
// 使用
function App() {
const { count, inc } = useCounter()
return <button
onClick={inc}
>
{count}
</button>
}
这样有几个问题:
- 函数污染了 store,我们在使用 useCounter 的时候,inc 函数总是在提示列表里面碍眼;
- 当我们需要在 hook 里面使用函数时,平白增加了依赖
function App() { const { count, inc } = useCounter() useEffect(() => { console.log(count) inc() // inc 是一个静态函数,按理来说是可以不用放在依赖里面的 }, [count, inc]) }
- 没有类型限制,无法阻止别人无意间修改 inc 函数:
// 在一些场合下,我们希望使用 replaceFlag,这时候就出问题了,把 inc 函数丢了 // replace flag: https://docs.pmnd.rs/zustand/guides/immutable-state-and-merging#replace-flag const replaceFlag = true useCounter.setState({ count: 3, }, replaceFlag)
这个怎么样?
如果能这样呢:
function App() {
const { count } = useCounter()
return <button
// 将 inc 变成 useCounter 的静态函数
onClick={useCounter.inc}
>
{count}
</button>
}
直接上代码
interface StaticProps {
[key: string]: unknown
}
export function withStatic<T extends object, S extends StaticProps>(
useStore: T,
staticFuncs: S
) {
const isDev = process.env.NODE_ENV === 'development'
const result = useStore as T & S
Object.keys(staticFuncs).forEach((key) => {
if (key in useStore) {
if (isDev) {
throw new Error(`property has exists: "${key}"`)
}
console.error(`property has exists: "${key}"`)
return
}
// @ts-expect-error 将 staticFuncs key 赋给 result
result[key] = staticFuncs[key]
})
return result
}
Usage
import { withStatic } from '@zimi/utils'
const useRawCounter = create(() => ({
count: 0,
}))
export const useCounter = withStatic(useRawCounter, {
inc: () => {
useRawCounter.setState((prev) => ({
count: prev.count + 1,
}))
},
})
// 调用
function App() {
const { count } = useCounter()
return <button
onClick={useCounter.inc}
>
{count}
</button>
}