Redux技术架构简介(二)-- 异步实现
说明:对Redux不了解的同学可先看看这篇文章Redux技术架构简介(一)
前言
这里说的Redux异步实现,是专指Redux中的异步Action实现,而不是指应用的异步实现,因为Redux本身只支持同步action,即发送action,state立即更新;那如果我在发送一个action后,需要state过一段时间再更新呢?按正常的思路redux是无法处理这种情况的,下面就来看看异步Action是如何实现的吧!
需要提的一点是,其实完全可以将异步逻辑写在View中,然后在回调函数中发送action。但是如果你要配合react一起使用,这样做就违背了react-redux的设计思想,即UI与逻辑的分离(具体的实现可以在下一篇文章中看到),而且当存在多个异步请求时也很难将异步逻辑抽象出来,所以异步逻辑应该由Redux架构实现,这样也就必须实现发送异步action了
1. 中间件(Middleware)
为了解决上面提到的问题,我们需要引入中间件的概念。
(1)时机
中间件执行的时机是在action发起之后,reducer执行之前。
即在dispatch一个action之后,经过一系列的中间件处理过程,再进行reducer。
(2)原理
本质上,中间件就是对dispatch函数的改造。当执行dispatch(action)时,会先调用中间件,进行一些内部逻辑处理,如:添加日志等,之后再执行dispatch。如果要支持中间件的链式调用,必须再返回一个dispatch。
下面是中间件的简单实现:
function logger(store) { // 这里的 next 必须指向前一个 middleware 返回的函数: const next = store.dispatch return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } }
(3) 在Redux中应用中间件
可以使用Redux的API—applyMiddleware直接使用中间件,代码如下:
import {applyMiddleware,createStore} from 'redux'; import {createLogger} from 'redux-logger';//log中间件 import thunk from 'redux-thunk';//将dispatch改造成可以接受函数作为参数的中间件 import indexPhotomainReducer from '../reducer/indexPhotomainReducer'; const logger = createLogger(); const store = createStore( indexPhotomainReducer, applyMiddleware(thunk, logger) );
由此可见,可以把applyMiddleware作为createStore的第二个参数传入,你所使用的中间件需要下载单独npm包,然后按顺序传入applyMiddleware函数中(注:中间件有顺序要求,需要看下每个中间件的使用文档,logger一般要放在最后)。
2. Redux异步实现
(1) Action设计
需要增加三种action
- 通知异步请求发起的action
- 异步请求成功的action
- 异步请求失败的action
示例代码如下:
export const requestPostsAction = () => { return { type: REQUEST_POSTS }; } export const receivePostsSuccessAction = (data) => { return { type: RECEIVE_POSTS_SUCCESS, data }; } export const receivePostsFailureAction = (error) => { return { type: RECEIVE_POSTS_FAILURE, error }; }
返回参数完全可以自定义。这3种action分别在请求开始前,请求成功后,请求失败后发送。
## (2) State设计
为了配合异步Action,可以在state树中增加2个属性:
- isFetching:表示是否正在处理异步请求。
- isValidate:表示数据的有效性,他的作用是在异步请求发送失败后,告诉View当前state的数据是过时的数据。
State属性可以凭自己喜好随意设计。设计好后可以这样编写reducer:
let sliderReducer = function (state = initialState, action) { switch(action.type){ case photomainAction.RECEIVE_POSTS_SUCCESS: return Object.assign({}, state, {photoData,videoData,isFetching:false,isValidate:true}); case photomainAction.RECEIVE_POSTS_FAILURE: return Object.assign({}, state, {isFetching:false,isValidate:false}); case photomainAction.REQUEST_POSTS: return Object.assign({}, state, {isFetching:true,isValidate:false}); default: return state; } }
(3) redux-thunk中间件
当设计好action和state后,我们想要达到的效果是--可以像同步一样dispatch一个action,而不用考虑异步逻辑。
为了达到这种效果,首先面临的问题是,异步逻辑放在哪里?这里只有2个选择:action中或reducer中(store是不适合处理数据的)。由于reducer必须是一个纯函数,他不适合处理像异步请求这样存在不确定的输出的逻辑。最后只能放在action中处理了。
这时,为了处理异步请求,action创建函数需要返回一个带有异步请求逻辑的函数,而不是一个对象了。而dispatch只能接受对象,不能接受函数作为参数,这样就面临又一个问题:如何让dispatch接受函数?
接下来就是redux-thunk中间件发挥作用的时候了,他可以让dispatch接受一个函数(原理就是上一节讲的,他其实是改写了dispatch函数),最终异步的action可以这样实现:
//定义一个action creator – fetchPosts export const fetchPosts = () => (dispatch, getState) => { dispatch(requestPostsAction()); return window.fetch("/photo/initPage").then(response=>{ if(response.ok){ return response.json(); }else{ dispatch(receivePostsFailureAction("error")); } }).then(data => { if(data){ dispatch(receivePostsSuccessAction(data)); }else{ dispatch(receivePostsFailureAction("error")); } }); }
这样就可以像同步一样发送action了,即:
dispatch(fetchPosts());
接下来只需静静等待view的更新就行了,这样就实现了整个Redux的异步流程。
本篇到此告一段落,下一篇介绍React与Redux整合技术。