【源码解析】redux-thunk

cmy0 2019-06-25

了解了Redux原理之后,我很好奇Redux中间件是怎么运作的,于是选了最常用的redux-thunk进行源码分析。

此次分析用的redux-thunk源码版本是2.2.0,redux源码版本是3.7.2。并且需要了解Redux原理

redux中间件都是由redux的applyMiddleware()方法所挂载的

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    //暴露给中间件的API,所以redux-thunk可以使用形如return (dispatch, getState)=>{}
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    //将暴露的API给中间件,调用中间件函数,生成中间件返回值函数(为什么返回值是函数?不是函数下面的compose()就报错了)
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    //组合全部中间件的返回值函数, chain是中间件返回值函数们
    //然后reduce起来的函数返回的也是函数,将store原来的dispatch传进去,dispatch函数也是接受一个action并返回一个action,作为中间件链的头部
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

可以看到redux主要做了以下事情:

  1. 对中间件们使用map,将dispatchgetState传递进去
  2. 使用compose将中间件组合起来,最后传入原生的store.dispatch

compose函数则是简单的将中间件进行串联调用

//compose(funcA, funcB, funcC) 等于 (args)=>funcA(funcB(funcC(args)))
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

最后我们回到redux-thunk

redux-thunk的代码很短,只有短短14行

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

它对外暴露的是const thunk = createThunkMiddleware();,也就是({dispatch, getState})=>{...}这个函数。

然后经chain = middlewares.map(middleware => middleware(middlewareAPI))传入dispatchgetStatechain里面是next=>{...},进行compose

compose(a,b,c)的返回值是函数(...args)=>a(b(c(...args))),这个函数经调用,传入参数store.dispatch,之后action会在中间件链上进行传递,只要保证每个中间件的参数是action并且将action传递给下一个中间件。

具体到redux-thunk中,它先检查action是否是函数,一般的action都是plain object,如果是函数就应该是由thunk处理。如果不是,传递给nextnext就是下一个中间件。

如果是函数,则调用这个函数并将dispatch, getState, extraArgument传入。这也就是为什么我们需要将thunk action生成函数(注意action和action生成函数的区别)写成() => (dispatch, getState) => {...},传入redux-thunk的action就是(dispatch, getState)=>{...}这个函数

相关推荐

jiangcs0 / 0评论 2020-04-19

空谷足音 / 0评论 2019-04-28