Redux的异步Action

Redux的Action本身只一个如下形式的对象:

{
    type: ADD_TODO,
    payload: 'Other args'
}

但是官方推荐我们使用一个ActionCreator的方法来返回一个action

export function addTodo(arg) {
  return {
    type: ADD_TODO,
    payload: arg
  }
}

这样做有两个好处

  1. 可以在actionCreator中传入参数来返回动态的action。
  2. 可以让actionCreator不再返回一个对象,而是返回一个方法,此时actionCreator成了一个thunk,也就是一个延迟执行值的方法,如果想知道thunk执行的结果,需要在合适的时候调用thunk()方法,如下面的例子。
export function addTodo(arg) {
  return function (dispatch) {
    return fetch(`http://www.xxx.com/a.json`)
      .then(
        response => response.json()
      )
      .then(json =>
        dispatch(receiveData(json))
      )
  }
}

上面的例子就创建了一个异步的action,actionCreator模糊了同步action和异步action的差异,你只需要调用方法即可,不用关心actionCreator返回的action类型。

现在的问题是Redux默认是只支持同步Action,也就是actionCreator应当返回一个对象,现在我们的Creator返回了一个方法,Redux并不会知道如何处理,所以这个时候我们就需要MiddleWare了。

MiddleWare的本质是重写dispatch方法,比如官网示例的以下方法即可让所有的dispatch都具有日志功能。

let next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

即使知道要重写dispatch方法,我们仍需要了解Redux的applyMiddleWare是如何处理middleware才能写一个中间件,这里摘一段applyMiddleWare的源码:

import compose from './compose';

export default function applyMiddleware(...middlewares) {
   return (next) => (reducer, initalState) => {
       let store = next(reducer, initalState);
       let dispatch = store.dispatch;
       let chain = [];

       var middlewareAPI = {
           getState: store.getState,
           dispatch: (action) => dispatch(action)
       };
       chain = middlewares.map( middleware => middleware(middlewareAPI));
       dispatch = compose(...chain)(store.dispatch);

       return {
           ...store,
           dispatch
       };
   }
}

通过代码可以知道middleware是一个方法,会被注入一个对象({getState, dispatch})执行,返回的chain中的元素仍然是一个方法,被聚合后注入store.dispatch再次调用,最终会得到一个新的、受到了污染的disptach,这个新的disptach应该有如下关系:

newDispatch = middleware ({getState, dispatch})(nextDispatch)

之所有第二个参数叫nextDispatch,是因为compose函数处理时,每经过一个chain都是返回一个基于下一个dispatch包装的新dispatch(chain中最后的方法最先执行),如果我们要写一个中间件,也就是重写dispatch方法,那么middleware的签名应该是这样:

middleware = ({getState, dispatch}) => nextDispatch => action => {}

那么thunk中间件如下:

thunk = ({getState, dispatch}) => nextDispatch => action =>{
    if(typeof action === 'function') {
        return action(dispatch)
    }
    return nextDispatch(action)
}

那么一个异步action实际就是一个方法,中间件会调用这个方法,同时会传一个dispatch给这个方法,rudux不关心这个方法要做什么,只要方法里面再调用dispatch方法即可让rudux更新状态.

React
JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里只属于前端工程师。