最近看了很多react-redux相关的repositories,一般来说在redux中异步获取存储数据是通过,redux相关的thunk-middleware,promise-middleware等中间件,通过把异步施加到action上来实现,通过reducer把请求来的数据存储到返回的状态中,然后通过Props把数据传入container中。
这里主要提到redux-async-connect:
这个包的主要特点是:
1.当点击某个路由时,直到和这个路由相关的所有promise都完成(包括resolve和reject),才会渲染路由所在页面的组件,这样的优点之一是极大程度提升用户体验,防止页面突然跳转(don’t jump when data was loaded)。如下为具体实现:通过render把Router和ReduxAsyncConnect联系到一起。
const component = (
<Router render={(props) =>
<ReduxAsyncConnect {...props} helpers={{client}} filter={item => !item.deferred} />
} history={history}>
{getRoutes(store)}
</Router>
);
2.asyncConnect decorator,用来修饰连接到路由的组件,例如如下,可以通过this.props.lunches直接取得异步取得的数据。
@asyncConnect([{
deferred: true, //这里是实现即使数据没有请求回来页加载页面的关键。
promise: ({store: {dispatch, getState}}) => {
if (!isLoaded(getState())) {
console.log('asyncConnect');
return dispatch(loadWidgets());
}
}
}])
其中helpers.client是一个http客户端从后台获取数据的插件,一般是应用superAgent和Axios,这里我们应用前者,下面是具体应用,通过promise绑定的数据,这样我们就可以应用then来处理返回的数据。
export default class ApiClient {
constructor(req) { //这里req指的是服务器request对象的serverRequest。
methods.forEach((method) =>
this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
const request = superagent[method](formatUrl(path));
//request指的是客户端请求的request.
if (params) {
request.query(params);
}
if (__SERVER__ && req.get('cookie')) {
request.set('cookie', req.get('cookie'));
//这里req指的是服务器request对象的serverRequest。
}
if (data) {
request.send(data);
}
request.end((err, { body } = {}) => err ? reject(body || err) : resolve(body));
}));
}
/*
* There's a V8 bug where, when using Babel, exporting classes with only
* constructors sometimes fails. Until it's patched, this is a solution to
* "ApiClient is not defined" from issue #14.
* https://github.com/erikras/react-redux-universal-hot-example/issues/14
*
* Relevant Babel bug (but they claim it's V8): https://phabricator.babeljs.io/T2455
*
* Remove it at your own risk.
*/
empty() {}
}
我们知道,在redux中,store,action,middleware的流程是,首先定义action(异步action的定义一般是 {type:,promise:}), 然后调用store的dispatch方法触发action,redux中只允许有一个store,和一个根reducer,通过combinereducer把多个reducer合并成一个reducer,被diapatch的action目标是进入reducer,但是进入reducer之前需要经过各个中间件,这种异步的情况来说,创建一个中间件例如createMiddleware,把superAgent所在的方法作为参数传递给中间件,在中间件里对action进行过滤,筛选出定义为{type:,promise:}形式的action,然后把superAgent方法作为参数传递进入action的promise方法中,把得到的数据保存起来传递进入reducer中。
这个superAgent方法做的事情如上,也就是处理http请求,根据运行的环境确定url是否带api/或者是config.host,config.port,然后后台根据这些url,通过路由来实现对中间件的过滤,分发对应的方法。而在createMiddleware中,根据请求返回数据的成功情况,来确定then的回调函数,如果成功就执行next({…rest, result, type: SUCCESS}),否则执行next({…rest, error, type: FAILURE}),然后数据会经过其他中间件,最后到达reducer,reducer通过action.result来取得返回的数据。