1. 背景
任何一个工具,技术的出现都是为了解决一个或多个问题。 同样,状态管理是为了解决(React为例,React没有全局状态):
- 数据共享
- 组件全局通信 当你在这两个问题上遇到了解决不了的问题后,可引入状态管理
2. useContext + useReduce
React hooks提供了两个hooks API,分别为:
useContextuseReduceuseContext可在顶层组件注入共享数据,其任意子组件均能使用,解决了数据共享问题。
useReduce提供返回的[state, dispatch]中dispatch可修改数据状态,组件A修改状态,组件B可拿到修改后的状态,解决组件全局通信问题。
使用方法
思路(Store组件中):
const [state, dispatch] = useReducer(reducer, initialState);利用useReducer来返回一个state(需要共享的数据)和dispatch(修改数据的方法)。- 通过
const Context = createContext();来创建一个Context对象。 - 使用
Context.Provider包裹需要共享数据的顶级组件,并注入数据state和dispatch方法。 - 通过
useContext(Context)来拿到共享数据,通过export useContext(Context)让其他子组件可以通过引入的方式拿到该共享数据。
代码结构为:
<Store>
<Parent store={[state, dispatch]}> // store是注入的数据
<ChildA>
<ChildB>
</Parent>
</Store>
完整代码如下所示:
1. Store.jsx
import React, { createContext, useReducer, useContext } from 'react';
const initialState = {count: 10};
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
// step 2 创建一个Context
const Context = createContext();
// step 4 获取数据并在最后export
const useStore = () => useContext(Context);
const StoreProvider = props => {
// step 1,useReducer返回state, dispatch
const [state, dispatch] = useReducer(reducer, initialState);
return (
// step 3 注入数据
<Context.Provider value={[state, dispatch]}>
{props.children}
</Context.Provider>
);
}
export { useStore, StoreProvider };
2. Parent.jsx
import React from 'react';
import { StoreProvider } from './store';
import ChildA from './ChildA';
import ChildB from './ChildB';
const Parent = () => {
return (
<div>
<p>这是react hooks自带的createContext, useReducer, useContext实现的</p>
<StoreProvider>
<ChildA />
<ChildB />
</StoreProvider>
</div>
);
}
export default Parent;
3. ChildA.jsx
import React from 'react';
import { useStore } from './store.js';
const ChildA = () => {
const [, dispatch] = useStore();
return (
<>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
export default ChildA;
4. ChildB.jsx
import React from 'react';
import { useStore } from './store';
const ChildB = () => {
const [state] = useStore();
return <p>{state.count}</p>;
}
export default ChildB;
3. hox
hox是由蚂蚁金服体验技术开发,号称是下一代React状态管理器,完全拥抱React Hooks,具有以下特点
- 仅一个API,上手简单
- 完美支持
TypeScript - 使用
custom Hooks来定义 model,完美契合 ReactHooks
使用方式
思路:
- 定义
Model:使用createModel()包裹任意一个custom hook(这里为useCounter) - 使用
Model: 通过useCounterModel()取到数据成功;
1. Store.js(定义store)
import { useState } from "react";
import { createModel } from "hox";
const useCounter = initialValue => {
const [count, setCount] = useState(initialValue ?? 10);
const decrement = () => setCount(count - 1);
const increment = () => setCount(count + 1);
return {
count,
decrement,
increment
};
}
// 任意一个custom Hook,用createModel包装后,就变成了持久化且全局共享的数据。包装后依旧是个custom Hook
// createModel(useCounter) 相当于const useCounterModel = createModel(useCounter);
export default createModel(useCounter);
// 带初始值的
// export default createModel(useCounter, 20);
2. Counter.jsx(使用store)
import React from 'react';
// useCounterModel 是一个真正的Hook,会订阅数据的更新。当点击 "+" 按钮时,会触发model的更新,并且最终通知所有使用useCounterModel的组件或Hook。
import useCounterModel from "./model";
const Counter = () => {
// createModel返回值是个Hook,可以按React Hooks的用法正常使用它
const counter = useCounterModel();
return (
<div>
<p>{counter.count}</p>
<button onClick={counter.increment}>+</button>
<button onClick={counter.decrement}>-</button>
</div>
);
};
export default Counter;
3. mobx-state-tree
mobx-state-tree是基于mobx开发的一个状态管理工具,核心思想是通过types生成Model,Model可定义一个对象结构,一个types可嵌套多个Model,从而生成一个动态树。可理解为该动态树对应的就是一个对象的数据结构
使用方式
思路:
- 通过
types生成一个counterStore的Model - 通过
counterStore.create()生成一个实例 observer修饰需要使用store的组件,通过props拿到数据
完整的代码如下:
1. store.js(定义store)
import React, { createContext, useContext } from 'react';
import {types} from 'mobx-state-tree';
console.log('types:', types);
const CounterStore = types
.model('counterStore', {
count: types.number
})
.actions(self => ({
increment() {
self.count++;
},
decrement() {
self.count--;
}
}));
export default CounterStore.create({count: 10});
2. Counter.js(使用store)
import React from 'react';
import {observer} from 'mobx-react';
// 要想获取更新后的值 必须有observer
const Counter = observer(props => {
// 组件传值使用
const {store} = props;
return (
<div>
<p>{store.count}</p>
<button onClick={store.increment}>+</button>
<button onClick={store.decrement}>-</button>
</div>
);
});
export default Counter;
5. 如何确定选择哪一个方案
首先,三种状态管理方案基本都能解决文章开头提出的两个问题,故可按照下面顺序选择:
- 简单项目不需要状态管理方案,使用状态提升即可
- 使用
hox,上手简单,契合hooks - 使用
useRedux + usecontext, 官方提供,上手难度高于hox,但也算简单 - 使用
mobx,上手难度较高
参考资源: