浅谈react状态管理 -flux/redux/mobx/dva
react自身是有数据流管理的,通过state和props来更新和传递状态,但是跨组件通讯、状态同步以及状态共享的实现并不理想,需要一个真正的数据流管理工具来专门管理数据。
一、Flux
Flux 的最大特点,就是数据的"单向流动"
Flux的使用

-
用户访问 View
-
View 发出用户的 Action
-
Dispatcher 收到 Action,要求 Store 进行相应的更新
-
Store 更新后,发出一个"change"事件
-
View 收到"change"事件后,更新页面
Flux将一个应用分成四个部分
- View: 视图层
- Action(动作):视图层发出的消息(比如Click)
- Dispatcher(派发器):用来接收Actions、执行回调函数,Dispatcher 只能有一个,而且是全局的
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux的优缺点
缺点:
- store之间需要建立依赖,需要token
- store 逻辑的修改无法热加载
二、redux
redux是flux演变而来的,是flux的加强版
redux的工作流程

// 用户发出 Action
store.dispatch(action);
// Store 自动调用 Reducer,并且传入两个参数,当前 State 和收到的 Action,Reducer 会返回新的 State
let nextState = todoApp(previousState, action);
// State 一旦有变化,Store 就会调用监听函数
store.subscribe(listener);
// listener可以通过store.getState()得到当前状态,触发重新渲染 View
function listerner() {
let newState = store.getState();
component.setState(newState);
}
redux的使用
组件结构
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../redux';
class Main extends Component {
render () {
return (
<div className="App">
<header className="App-header">
<p onClick={() => this.props.valueAdd()} > 点击 + 1 </p>
<p onClick={() => this.props.valueReduce()} > 点击 - 1 </p>
<div> i am {this.props.number} </div>
</header>
</div>
)
}
}
export default connect(
({number}) => ({number}),
dispatch => bindActionCreators(action, dispatch)
)(Main)
redux-saga
解决异步等问题,根据相应中间件调整store的生成方式
import { applyMiddleware, createStore } from 'redux';
import createSagaMiddleware from "redux-saga";
import rootSaga from "./saga"
import reducer from './reducer'
let sagaMiddleware = createSagaMiddleware()
export const store = createStore(
reducer,
applyMiddleware(sagaMiddleware),
);
sagaMiddleware.run(rootSaga); // 开始执行rootSaga
sage文件
function * add () {
// 派发Action
yield put({ type: ACTION_ADD })
}
function * reduce () {
yield put({type: ACTION_REDUCER})
}
function * watchAdd() {
// 监听派发给仓库的动作,如果动作类型匹配的话,会执行对应的监听生成器
yield takeEvery(ACTION_ADD_SAGA, add)
yield takeEvery(ACTION_REDUCER_SAGA, reduce)
}
export default function * rootSaga() {
yield watchAdd()
}
| put | 派发Action, 可以理解成为redux中的dispatch函数 |
|---|---|
| call | 调用函数执行,阻塞 |
| fork | 调用函数执行,不会阻塞 |
redux图解
示例背景: TodoList = Todo list + Add todo button
React 只负责页面渲染, 而不负责页面逻辑, 页面逻辑可以从中单独抽取出来, 变成 store

- 状态及页面逻辑从里面抽取出来, 成为独立的 store, 页面逻辑就是 reducer
- 和都是 Pure Component, 通过 connect 方法可以很方便地给它俩加一层 wrapper 从而建立起与 store 的联系: 可以通过 dispatch 向 store 注入 action, 促使 store 的状态进行变化, 同时又订阅了 store 的状态变化, 一旦状态有变, 被 connect 的组件也随之刷新
- 使用 dispatch 往 store 发送 action 的这个过程是可以被拦截的, 自然而然地就可以在这里增加各种 Middleware, 实现各种自定义功能, eg: logging
这样一来, 各个部分各司其职, 耦合度更低, 复用度更高, 扩展性更好
加入 Saga

上面说了, 可以使用 Middleware 拦截 action, 这样一来异步的网络操作也就很方便了, 做成一个 Middleware 就行了, 这里使用 redux-saga 这个类库, 举个栗子:
- 点击创建 Todo 的按钮, 发起一个 type == addTodo 的 action
- saga 拦截这个 action, 发起 http 请求, 如果请求成功, 则继续向 reducer 发一个 type == addTodoSucc 的 action, 提示创建成功, 反之则发送 type == addTodoFail 的 action 即可
redux的优点
- 状态持久化:globalstore可以保证组件就算销毁了也依然保留之前状态;
- 状态可回溯:每个action都会被序列化,Reducer不会修改原有状态,总是返回新状态,方便做状态回溯;
- Functional Programming:使用纯函数,输出完全依赖输入,没有任何副作用;
- 中间件:针对异步数据流,提供了类express中间件的模式,社区也出现了一大批优秀的第三方插件,能够更精细地控制数据的流动,对复杂的业务场景起到了缓冲地作用;
redux的缺点
- 繁重的代码模板:修改一个state可能要动四五个文件,可谓牵一发而动全身;
- store里状态残留:多组件共用store里某个状态时要注意初始化清空问题;
- 无脑的发布订阅:每次dispatch一个action都会遍历所有的reducer,重新计算connect,这无疑是一种损耗;
- 交互频繁时会有卡顿:如果store较大时,且频繁地修改store,会明显看到页面卡顿;
- 不支持typescript;
三、mobx
MobX的数据驱动解构: action–(update)–>state–(update)–>computed–(trigger)–>reaction
Mobx的核心原理是通过action触发state的变化,进而触发state的衍生对象(computed value & Reactions)
核心概念

Mobx的核心就是通过observable观察某一个变量,当该变量产生变化时,对应的autorun内的回调函数就会发生变化。
mobx异步处理
Mobx在异步处理上并不复杂,在严格模式下,对于异步action里的回调,若该回调也要修改observable的值,那么该回调也需要绑定action
mobx的优缺点
优点:
- 响应式编程,学习成本低,代码量少,开发难度低,渲染性能好
四、Dva
Dva 是基于 React + Redux + Saga 的最佳实践沉淀, 做了 3 件很重要的事情, 大大提升了编码体验:
- 把 store 及 saga 统一为一个
model的概念, 写在一个 js 文件里面 - 增加了一个 Subscriptions, 用于收集其他来源的 action, eg: 键盘操作
- model 写法很简约
model代码示例
app.model({
namespace: 'count',
state: {
record: 0,
current: 0,
},
reducers: {
add(state) {
const newCurrent = state.current + 1;
return { ...state,
record: newCurrent > state.record ? newCurrent : state.record,
current: newCurrent,
};
},
minus(state) {
return { ...state, current: state.current - 1};
},
},
effects: {
*add(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'minus' });
},
},
subscriptions: {
keyboardWatcher({ dispatch }) {
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
},
},
});
dva的图解

参考链接: