ReactUse createGolaState 源码解析
说明
useGlobalState:一个创建全局共享状态的 react hook。
const useGlobalValue = createGlobalState<number>(0);
const CompA: FC = () => {
const [value, setValue] = useGlobalValue();
return <button onClick={() => setValue(value + 1)}>+</button>;
};
const CompB: FC = () => {
const [value, setValue] = useGlobalValue();
return <button onClick={() => setValue(value - 1)}>-</button>;
};
const Demo: FC = () => {
const [value] = useGlobalValue();
return (
<div>
<p>{value}</p>
<CompA />
<CompB />
</div>
);
};
源码
// hookState.ts
export function resolveHookState<S>(nextState: IHookStateInitAction<S>): S;
export function resolveHookState<S, C extends S>(
nextState: IHookStateSetAction<S>,
currentState?: C
): S;
export function resolveHookState<S, C extends S>(
nextState: IHookStateResolvable<S>,
currentState?: C
): S;
export function resolveHookState<S, C extends S>(
nextState: IHookStateResolvable<S>,
currentState?: C
): S {
if (typeof nextState === 'function') {
return nextState.length ? (nextState as Function)(currentState) : (nextState as Function)();
}
return nextState;
}
// useEffectOnce.ts
const useEffectOnce = (effect: EffectCallback) => {
useEffect(effect, []);
};
// useIsomorphicLayoutEffect
const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
export function createGlobalState<S = any>(
initialState: IHookStateInitAction<S>
): () => [S, (state: IHookStateSetAction<S>) => void];
export function createGlobalState<S = undefined>(): () => [
S,
(state: IHookStateSetAction<S>) => void
];
export function createGlobalState<S>(initialState?: S) {
const store: {
state: S;
setState: (state: IHookStateSetAction<S>) => void;
setters: any[];
} = {
state: initialState instanceof Function ? initialState() : initialState,
setState(nextState: IHookStateSetAction<S>) {
store.state = resolveHookState(nextState, store.state);
store.setters.forEach((setter) => setter(store.state));
},
setters: [],
};
return () => {
const [globalState, stateSetter] = useState<S | undefined>(store.state);
useEffectOnce(() => () => {
store.setters = store.setters.filter((setter) => setter !== stateSetter);
});
useIsomorphicLayoutEffect(() => {
if (!store.setters.includes(stateSetter)) {
store.setters.push(stateSetter);
}
});
return [globalState, store.setState];
};
}
export default createGlobalState;
思考
函数重载
我比较喜欢这种函数重载,使用的时候很方便,使用时可以灵活传参。
export function createGlobalState<S = any>(
initialState: IHookStateInitAction<S>
): () => [S, (state: IHookStateSetAction<S>) => void];
export function createGlobalState<S = undefined>(): () => [
S,
(state: IHookStateSetAction<S>) => void
];
export function createGlobalState<S>(initialState?: S) {}
uselayoutEffect 和 useEffect
uselayoutEffect 在组件内容渲染之前执行。
useEffect 在组件内容渲染之后执行,返回的函数在组件卸载时执行。
源码中利用 uselayoutEffect 在组件渲染前将 stateSetter 放入 store.setters 数组中,利用 useEffect 在组件
卸载时去掉 store.setters 中的 stateSetter。
整体代码思路解构很清晰,创建一个全局对象,当组件渲染时基于全局对象创建组件的状态,通过执
行全局对象的 setters 队列来更新对应的组件状态。
总结
和 VueUse 的 useGlobalState 源码简单对比下:
代码结构思路上更新喜欢 ReactUse useGlobalState 的写法,整体实现思路很清晰,灵活度更高些。
VueUse useGlobalState 的实现上更简单,不用单独给组件创建和释放状态,而且可以创建全局状态相关的 computed 和 watch。但 vue 组件卸载时会自动处理释放 computed 和 watch 的 effects,所以要通过 effectScope 创建 effect 作用域避免释放 computed 和 watch 的 effects,而这样导致代码结构并不是很好理解,官网对 effectScope 的解释也很简单。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?