- Published on
- 约 791 字
实现一个简单的zustand状态管理器
- Authors
- Name
- 小辉辉
背景
从官方zustand文档我们可以了解到这个状态管理器有以下几个优点
- 使用简单无歧义
- 主要使用自定义hooks模式来管理状态
- 不需要用context provider包裹应用
- 支持通过订阅形式来实现临时更新,但不用重新渲染整个组件
上面几个优点里,我对第二点和第三点尤其觉得方便,特别是用过redux的人来说。
抽空就瞄了下zustand内部的实现,初步看完以后发现实现起来果然很简单的,前提是要对react本身提供的useSyncExternalStore
hook有一定的了解,详细可以查看官方使用文档。
这里简单说明下用法,该hook接收3个参数,subscribe, getSnapShot, getServerSnapshot, 其中我们用到前面两个参数。第一个是更新的订阅方法,用于store变化时重新渲染当前组件;第二个参数提供一个方法返回当前的store值。
实现
好了,接下来直接上完整的代码,一共50行不到
import React, { useSyncExternalStore } from 'react';
const create = (fn: any) => {
let listeners: any[] = [];
let state: any;
const set = (update: any) => {
state = {
...state,
...update(state),
};
listeners.forEach((listener) => {
listener();
});
};
state = fn(set);
return (selector: any) => {
const value = useSyncExternalStore(
(listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter((l) => l !== listener);
};
},
() => state,
);
return selector(value);
};
};
const useMaoStore = create((set: any) => ({
mao: 0,
change: () =>
set(() => ({
mao: Math.random(),
})),
}));
const P = () => {
const mao = useMaoStore((state: any) => state.mao);
const changeMao = useMaoStore((state: any) => state.change);
return <div onClick={changeMao}>{mao}</div>;
};
export default P;
代码主要实现create方法,用于生成useMaoStore这个自定义hook,在组件里使用该hook获取store钟对应的属性或者更新方法, 也就是我们所谓的selector。
接下来我们看看create方法是怎么实现的,内部主要分三大部分:
一部分是维护一个subscribe的存储数组我们命名为listener,还有一个state变量也就是存储我们所谓的store。
第二部分就是store对外暴露的更新方法,该方法有一个入参update,由外部传入通过和调用该方法来返回新的state和当前的state进行一个合并操作来替换旧store,同时为了实现组件的自动渲染,我们还需要手动依次调用listener数组中订阅的更新方法了。
第三部分就是需要返回一个自定义hook,该hook接收一个seletor参数,函数类型,除此之外调用react自身提供的useSyncExternalStore hook来实现绑定关系,在第一个参数中我们需要将每个更新值push到listener数组中,同时还需返回一个函数在组件销毁时清空lisnter数组中订阅的当前listener,第二个参数只需返回当前的state变量值即可,它的返回值作为selector方法的入参。
可以发现整个实现主要还依赖了js的闭包功能,由此才能实现state和listener变量可以在不同的store之间实现隔离。