什么是状态管理:
状态管理就是管理应用程序中会变化的数据的一套方法或工具
在react下实现一套状态管理
- 组件之外可以共享数据状态
- 数据更新,会触发相关方法
- 数据更新触发ui更新
以这几点我们可以基于React框架实现一套自己的状态管理工具
ts
//store.ts
interface Action {
type: string,
payload?:any
}
const creatData = function (init: any) {
let data = init; //初始化数据
let deps:Array<Function> = []; //需要订阅的方法
function subscribe(fn:Function) {
//开始订阅
deps.push(fn)
return () => { //取消订阅
let index = deps.indexOf(fn);
deps.splice(index,1)
}
}
function setDataByAction(action: Action) {
const currentData = reducer(data,action) //通过reducer 方法更新状态
data = currentData;
deps.forEach(fn => fn()) //通知所有订阅者
}
function getData() {
return data
}
return {subscribe,setDataByAction,getData}
}
const init = {
count: 0
}
const reducer = (data:any, action:Action) => {
switch(action.type) {
case 'add_count':
return {count: data.count + 1}
case 'change_count':
return {count : action.payload}
default:
return data;
}
}
export const objData = creatData(init);
消费阶段
ts
import { objData } from "./store";
const addAction = {
type: 'add_count',
}
const changeAction = {
type: 'change_count',
payload: 100
}
const Button1 = () => {
const handleAdd = () => {
objData.setDataByAction(addAction)
}
return <button onClick={handleAdd}>加</button>;
};
const Button2 = () => {
const handleChange = () => {
objData.setDataByAction(changeAction)
}
return <button onClick={handleChange}>修改</button>;
};
const App = () => {
const [count, setCount] = useState<number>(0);
useEffect(() => {
objData.subscribe(() => { //添加监听方法 触发ui更新
let data = objData.getData();
setCount(data.count);
});
}, []);
return (
<div>
<div>当前数字状态:{count}</div>
<Button1></Button1>
<Button2></Button2>
</div>
);
};
这里基本实现了状态管理,其实这也是redux的实现原理,我们继续完善,现在的如果所有的Action全都由同一个reducer方法去管理,基于性能以及可维护性都不好,接下来实现合并reducer
ts
const init = {
counter: {
count: 0,
},
info: {
name: '张三',
age: 16
}
}
const counterReducer = (data: any, action: Action) => {
console.log(data,action)
switch (action.type) {
case 'add_count':
return { count: data.count + 1 }
case 'change_count':
return { count: action.payload }
default:
return data;
}
}
const infoReducer = (data: any, action: Action) => {
switch (action.type) {
case 'change_age':
return { name: action.payload }
case 'change_person':
return { ...action.payload }
default:
return data;
}
}
interface Reducers {
counter: Function,
info: Function,
[key:string]: Function,
}
const combineReducer = function (reducers:Reducers) {
return (data: any, action: Action) => { //返回合并后的reducer函数
let keys = Object.keys(reducers)
const nextState = {} as Reducers; //定义变量存储处理后的data
keys.forEach(key => { //循环执行所有的reducer 合并值
let reducerAction = reducers[key];
let cur_state = data[key];
const next = reducerAction(cur_state, action)
nextState[key] = next
})
return nextState
}
}
const reducer = combineReducer({
counter: counterReducer,
info: infoReducer
})
这个时候虽然也实现了合并,但是数据的更新我们需要大量的订阅函数以及useState定义,模仿react-redux 结合context实现子组件自动更新
ts
//app.tsx
import myStore from "./store";
const ReduxContext = createContext<null | any>({});
const connect =
(mapStateToProps: Function, mapDispatchToProps: Function) =>
<P extends object>(MyComponent) => {
return function ConnectComponent(props: P) {
const _store = useContext(ReduxContext);
const foceUpdate = () => {
setBool(pre => !pre);
};
useEffect(() => {
const unsubscribe = _store.subscribe(() => foceUpdate()); //监听函数,store数据变动强制触发消费store的组件进行更新
return () => { //React.StrictMode 模式下必须添加清理函数,否则组件不会触发更新
unsubscribe();
};
}, []);
const [, setBool] = useState(false);
return (
<ReduxContext.Consumer>
{store => (
<MyComponent
{...mapDispatchToProps(store.setDataByAction)}
{...mapStateToProps(store.getData())}
{...props}
/>
)}
</ReduxContext.Consumer>
);
};
};
const ConnectTest = ({ counter, handleAdd }) => {
return (
<div>
<div>当前数字: {counter.count}</div>
<button onClick={() => handleAdd()}>添加</button>
</div>
);
};
const mapStateToProps = state => {
return { counter: state.counter };
};
const mapDispatchToProps = dispatch => {
return {
handleAdd: () => {
console.log("触发更新");
dispatch({ type: "add_count" });
},
};
};
const ConnectCom = connect(mapStateToProps, mapDispatchToProps)(ConnectTest);
const App = () => {
return (
<ReduxContext.Provider value={myStore}>
<ConnectCom></ConnectCom>
</ReduxContext.Provider>
);
};