在 redux 的初期使用中,我们在 react 组件中进行异步处理,并在 then 中同步分发 action,在 reducer 中实时更新数据,但这样容易出现冗余代码且不易于后期维护,后前我们使用了 redux-thunk ,将每个 thunk 作为异步处理的流程块 ,随着业务的复杂我们发现对于更复杂的流程控制,redux-thunk 也是力不从心,接着我们发现了 redux-saga,并对其强大的异步处理流程控制着迷,但这个时候我们发现我们的每个模块单元下的目录结构如下:
每个 model 都有独立的维护机制,各自负责属于自己的一块 state-tree。每个 model 将对其独立的 state-tree 进行维护的 reducers 进行封装,同时把负责业务逻辑的 saga 放入 model 中,并为每个 model 增加 namespace 。看起来的确很完美,但是我们发现其中这个 model (我们统称为 saga-model) 的处理( dva )与 react 进行了捆绑,同时由于我们有一套自己实现的项目启动工具,想要将这个 saga-model 的优秀思想进行结合。故我们将其进行抽取,同时进行改造。
-
独立出来的 model 处理器成为 sagaModel ,通过实例化 SagaModel 拿到 sagaModel。
-
可以很明显的入参 initialState
initialReducer
initialMiddleware
而不需要 dva 那样晦涩。
-
每个 model 中的代码结构如下:
{
namespace:'index',
state:{
name:'Tim'
},
reducers:{
update:function(state,{payload}){
return{ ...state,name:payload.name };
}
},
sagas:{
*updateName({payload},effects){
yield effects.put({
type:'update',
payload,
});
}
}
}
可以发现与 dva 的 model 基本一致,但是把 dva 中的 model 的 effects
字段换成了 sagas
因为根据 redux-saga 的官方文档介绍,这个叫法更符合其用意。
同理,在 plugins 中,也将 dva 中的 onEffect
改成了 onSaga
。
this.hooks = {
onError: [],
onStateChange: [],
onAction: [],
onHmr: [],
onReducer: [],
onSaga: [],
extraReducers: [],
extraEnhancers: [],
};
-
在每个 model 内的 saga,默认执行类型是 takeEvery
可以像 dva 一样将每个 saga 传入数组格式,更换其执行类型:
{
namespace:'index',
state:{
name:'Tim'
},
reducers:{
update:function(state,{payload}){
return{ ...state,name:payload.name };
}
},
sagas:{
updateName:[*({payload},effects)=>{
yield effects.put({
type:'update',
payload,
});
},{ type: 'takeEvery' }],
updateAddress:[*({payload},effects)=>{
yield
},{ type: 'takeLatest' }]
}
}
支持的类型与 dva 相同 watcher
takeLatest
throttle
takeEvery
默认是 takeEvery
在 dva 中,每个 saga 都允许省略当前 model 的 namespace 前缀直接分发( put )当前 reducer 的名字作为 actionType ,但是同样的特性不支持 take( 在 watcher
中自己决定接受什么 action),这里将其改进为支持该特性。
{
namespace:'index',
state:{
name:'Tim'
},
reducers:{
update:function(state,{payload}){
return{ ...state,name:payload.name };
}
},
sagas:{
watchUpdateName:[*({payload},effects)=>{
yield effects.take('update_name');
yield effects.put({
type:'update',
payload,
});
},{ type: 'watcher' }],
}
-
为每个 saga 内部的 this 提供指向当前的 model。
-
不再提供与 react,react-router 的绑定,在 dva 中可以直接 start 启动,而这里需要你自己根据实际情况进行处理, 实例化 SagaModel 后通过 sagaModel 的 store
方法获取配置完成的 store,自己进行下一步的处理,如需要与 react-router 进行绑定,可以参考 react-router-redux-saga-model
以及其对应的案例。
-
API 的命名改变,具体如下。
-
SagaModel
model 处理器类,处理过程与 dva基本相似,入参均为可选:
-
initialState :Object 传入 createStore 的默认 state
-
initialReducer :Object 不包含在 model 中的其他 reducers
-
initialMiddleware :Array 其他 middleware, logging 等各种
-
initialModels :Array 在获取 store 时启动所有的 model,正常情况下推荐使用改默认入参将所有的 model 进行启动,如果需要异步启动可以使用 sagaModel.register
方法。
-
history :Object 通过 history
构造的实例,可以用于在 model 的 substrtions 中使用。
import {SagaModel} from 'redux-saga-model';
const sagaModel = new SagaModel({initialState, initialReducer, initialMiddleware, initialModels,history});
const store = sagaModel.store();
-
sagaModel.use
使用插件
import sagaModel from 'redux-saga-model';
import someCrossSliceReducer from 'somewhere';
import reduceReducers from 'reduce-reducers'
sagaModel.use({
onReducer:(reducer)=>{
return reduceReducers(reducer, someCrossSliceReducer);
},
})
插件类型:如前面所述,基本与 dva 一致,除了把 onEffect
改成了 onSaga
。
hooks = {
onError: [],
onStateChange: [],
onAction: [],
onHmr: [],
onReducer: [],
onSaga: [],
extraReducers: [],
extraEnhancers: [],
}
-
sagaModel.plugin
获取所有插件
import sagaModel from 'redux-saga-model';
const plugins = sagaModel.plugin();
-
sagaModel.setHistory
设置 history
import createBrowserHistory from "history/createBrowserHistory";
import sagaModel from 'redux-saga-model';
sagaModel.setHistory(createBrowserHistory());
-
sagaModel.history
获取 history
import createBrowserHistory from "history/createBrowserHistory";
import {ConnectedRouter} from "react-router-redux";
import sagaModel from 'redux-saga-model';
sagaModel.setHistory(createBrowserHistory());
export default (props)=>{
return (
<ConnectedRouter history={sagaModel.history()}>
{ props.children }
</ConnectedRouter>
);
}
-
sagaModel.store
获取配置完成的 store,并启动所有的已存在的 models
import {Provider} from "react-redux";
import sagaModel from 'redux-saga-model';
import APP from './app';
export default ()=>{
return (
<Provider store={sagaModel.store()}>
<APP />
</Provider>
);
}
-
sagaModel.register
注册一个 model,可以在任何时刻调用。
import sagaModel from 'redux-saga-model';
import userModel from 'users/userModel';
import indexModel from 'index/indexModel';
import todoModel from 'todo/todoModel';
sagaModel.register(indexModel);
const store = sagaModel.store();
sagaModel.register(todoModel);
new Promise((resolve,reject)=>{
setTimeout(()=>{
sagaModel.register(userModel);
resolve();
},1000);
});
-
sagaModel.dump
卸载一个指定 namespace 的 model
import userModel from 'users/userModel';
import sagaModel from 'redux-saga-model';
sagaModel.register(userModel);
sagaModel.dump(userModel.namespace);
-
sagaModel.models
获取所有的 models
import sagaModel from 'redux-saga-model';
console.log(sagaModel.models().length);
-
其他内部 API