从1年半前开始使用React Hook,看着大串大串的reducer方法,我在做项目的时候在想,redux这样的模式是否过于繁琐,比如说给管道写数据,非得调用一个dispatch方法,另外还要做一个reducer的数据合并方法,加上各种中间件容易整出一些头疼的代码。
Redux本质上是一种全局订阅模式
如图所示,redux提供了一个订阅管道,ComponentA / ComponentB / ComponentC 通过dispatch给redux写数据,reducer合并处理数据,保存到redux states,一旦数据改变订阅数据的组件做响应式更新。
react 16.8版本推出了hook写法,出现了useState的写法,我们能不能推出一种新的简化的写法取代Redux。
Global State 是不错的方案,国外有一些版本,我这里也给出我的版本。
就是这么简单的一段(^_^)
// global-state.ts
import { useEffect, useState } from 'react';
declare const process: any;
export const GLOBAL_STATE = {
DEBUG: false
};
export const makeGlobalState = <T extends unknown>(initValue: T, name?: string, filter?: (val: T) => T) => {
const globalStates: any[] = [];
let currentValue = initValue;
return (): [T, (val: T) => void] => {
const [value, setValue] = useState(currentValue);
if (!globalStates.includes(setValue)) {
globalStates.push(setValue);
}
useEffect(
() => () => {
if (process.env.NODE_ENV && process.env.NODE_ENV === 'development') {
globalStates.splice(globalStates.indexOf(setValue) - 1, 2);
} else {
globalStates.splice(globalStates.indexOf(setValue), 1);
}
},
[]
);
const setAllStateValue = (value: T) => {
if (filter) {
value = filter(value);
}
if (GLOBAL_STATE.DEBUG) {
console.log('SET ' + name, value);
}
currentValue = value;
globalStates.forEach((setVal) => {
setVal(value);
});
};
return [value, setAllStateValue];
};
};
复制代码
做法是给每个component的local state做一个收集器,把setState的方法收集起来,在每次调用这个global state的设置方法的时候,遍历执行所有的set state,达到给所有component刷新的目的。
-
有添加就要有销毁,代码中的useEffect用于在component销毁时移除setState方法。这里要注意的是因为development模式下component渲染2次(会添加2次),所以这里需要做一些处理。
-
函数makeGlobalState还提供了一个filter功能,可以做类似reducer处理数据的功能,说得高大上一点就是可以定义一个中间件什么的….。第二个参数name则是用于debug打印。
使用的时候,先要创建一份文件,去定义所有的global-state,给初始值。
// my-global-states.ts
import { makeGlobalState } from './global-state';
export const useCounter = makeGlobalState(1, 'Counter', (val) => {
return val > 10 ? 10 : val;
});
export const useUserInfo = makeGlobalState({ name: 'John', age: 30 });
复制代码
然后就可以在不同的component上使用了
// CompA.tsx
import React from 'react';
import { useCounter } from './my-global-states';
export const CompA = () => {
const [count, setCount] = useCounter();
return (
<div>
<div>{count}</div>
<button
onClick={() => {
setCount(count + 1);
}}
>
加1
</button>
</div>
);
};
复制代码
// CompB.tsx
import React from 'react';
import { useCounter } from './my-global-states';
export const CompB = () => {
const [count, setCount] = useCounter();
return (
<div>
<div>{count}</div>
<button
onClick={() => {
setCount(5);
}}
>
设为5
</button>
</div>
);
};
复制代码
// App.tsx
import React from 'react';
import './App.css';
import { CompA } from './CompA';
import { CompB } from './CompB';
import { GLOBAL_STATE } from './global-state';
GLOBAL_STATE.DEBUG = true;
function App() {
return (
<div className="App">
<div>
<CompA />
</div>
<div>
<CompB />
</div>
</div>
);
}
export default App;
复制代码
运行结果,任何一个使用了useCounter的组件调用了 setCounter 方法,其他的订阅了couter值的组件也一起刷新了。