前端状态管理思考与实践
September 11, 2017
背景
再过去的两年我一直在维护大量的 OA 应用,所以在 redux 的基础上封装了路由,订阅器,action 生命周期等功能,方便各个 OA 系统开箱即用使用。 一个极简的 demo 如下: 注: 其中的 reducers 有 start,next,throw,finish 四个生命周期,方便进一步简化 redux 异步处理的情况下的样板代码
import React from 'react';
import rab, {connect, createModel, put, call} from 'rab';
import {Router, Route} from 'rab/router';
function stop(time) {
return new Promise((res, rej) => {
setTimeout(function () {
res();
}, 2000);
});
}
const app = rab();
// 2. Model
let count = createModel({
namespace: 'count',
state: {
num: 0,
loading: false
},
reducers: {
add(state, action) {
console.log(action.payload);
return Object.assign({}, state, {num: state.num + 1})
},
asyncAdd(state, action) {
return Object.assign({}, state, {num: state.num + action.payload})
},
asyncMinus: {
start(state, action){
return Object.assign({}, state, {loading: true});
},
next(state, action){
return Object.assign({}, state, {num: state.num + action.payload})
},
throw(state, action){
return Object.assign({}, state, {num: state.num + action.payload})
},
finish(state, action){
return Object.assign({}, state, {loading: false});
}
}
},
actions: {
asyncAdd: (a, b, c) => async ({getState, dispatch}) => {
await stop();
return 100;
},
async asyncMinus() {
await stop();
return -100;
}
},
subscriptions: {
init({history, dispatch}){
history.listen((location) => {
console.log('init------------>', location)
})
}
}
})
app.addModel(count);
// 3. View
const App = connect(({count}) => ({
count
}))((props) => {
return (
<div>
<h2>{ props.count.num }</h2>
<h2>{ !props.count.loading ? 'finish' : 'loading' }</h2>
<button key="add" onClick={() => {
put({type: 'count.add', payload: {a: 1}});
}}>+
</button>
<button key="asyncadd" onClick={() => {
props.dispatch(count.actions.asyncAdd());
}}>ASYNC ADD
</button>
<button key="asyncminus" onClick={() => {
put({type: 'count.asyncMinus', payload: {a: 1, n: 2}});
}}>ASYNC Minus
</button>
</div>
);
});
// 4. Router
app.router(({history}) => {
return (
<Router history={history}>
<Route path="/" component={App}/>
</Router>
);
});
// 5. Start
app.start('#demo_container');
问题
在今年开始,我主要负责实现一系列的大象富应用,如实时协作的 excel,word,ppt,脑图等等。考虑到后期的维护性,我们全面拥抱了 TS ,这时候发现之前的状态管理存在一些问题:
- 存在 字符串 进行 dispatch 的情况,如上面例子中第 57 行的
count.asyncMinus
- 整体集成方式过于耦合,在富应用的开发环境下,我们更希望能基于依赖注入的方式将各个模块解耦
- 局部状态过于复杂,在实际应用开发过程中,会发现存在很多局部的 UI 状态,这些是和业务建模相关的数据无关的,但是由于我们组件化拆分的比较彻底,就会导致如果不放在全局统一的数据源下,就需要层层的 props 进行传递,代码的维护性和可读性都会有影响
- 需要自行的处理
阅读量
Written by xi ming You should follow him on Github