关于redux中的middleware的一个问题

我知道middleware的代码是类似这样的:

export default function thunkMiddleware({ dispatch, getState }) {                        
    return next => action => {      
        if (typeof action === 'function') {               
             return action(dispatch, getState);      
        }       
        return next(action);    
   }; 
}

applyMiddleware的代码是这样的:

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

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    // 将middleware初始化,些时chain里是Array<next=>action=>doSomething>
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

compose的实现:

export default function compose(...funcs) {
  return (...args) => {
    if (funcs.length === 0) {
      return args[0]
    }

    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)

    return rest.reduceRight((composed, f) => f(composed), last(...args))
  }
}

接收一堆参数fns,返回另一个函数。在这个函数里,将参数中的函数逐个从右到左执行,上一个执行的结果将做为下一个执行的参数。

即compose(f, g, h)(100)相当于f(g(h(100))). 而对于上面的

dispatch = compose(...chain)(store.dispatch)

最后会是默认的dispatch一个action(action)。

假如有很多个middleware,那怎么把参数从一个middleware传到下一个middleware?

比如说redux-thunk,它的代码是这样的:

export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}

下面的一个案例:

var asyncSayActionCreator = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

但我dispatch(asyncSayActionCreator('hello world'));

可以看到它最后是直接

 dispatch({
                    type: 'SAY',
                    message
                })
                

直接dispatch了(这个dispatch是未经改造的),又没有返回其它的参数给其它的middleware,那其它的middleware完全没起作用?即使其它的middleware也可以对function类型的action处理?回看redux-thunk的代码,如果action是function,它完全不理它下面的middleware了啊..........

求解答.......

阅读 3.7k
2 个回答

问题太长了,没人会看吧。

不属于目前这个middleware就用next(action)传到下一个middleware。

applyMiddleware可以同时传入多个middleware为参数,实际上有顺序,从左至右一个个执行。前面能执行完成后面自然不会再用其它的middleware执行。

每个middleware可以得到store的dispatch与getState为传参,最后会得到一个函数(function)型的action,然后以next(action)往下一个middleware执行。在连锁中的最后一个middleware将会得到真实的store的dispatch方法作为next的参数,以此结束整个连锁。

这样说好了,因为你直接用了redux-thunk,它会把所有action都是函数类型在这里面执行掉,所以会有这些疑问,middleware可以不用redux-thunk而是自己撰写,没有想像中复杂,自己撰写可以更清楚的理解middleware是怎么运作的。下面简单用个例子说明,出自于这个教程文章:

action.js

export const onFecthData = () => ({ type: 'FETCH_ITEMS', cb: (response, dispatch) => dispatch(onInitData(response))})
export const onInitData = (items) => { return ( {type: 'INIT_ITEMS', items} ) }

这是个Action Creator,要用两个不同的函数,一个是有带回调函数作为payload的onFecthData,另一个是配合用的onInitData,它才是真正要把数据往reducer作最后更动用的。

fetchMiddleware.js

const fetchMiddleware = store => next => action => {
  if(action.type !== 'FETCH_ITEMS') return next(action)

  fetch('http://localhost:3000/sample.json')
  .then(response => response.json())
  .then(json => action.cb(json, store.dispatch))
  .catch((err) => { throw new Error(err.message) })
}

export default fetchMiddleware

这是个middleware,它用action.type区分是不是在这个middleware该处理的,如果不是就往下个middleware送,也就是用next(action)。假设是这个middleware该处理的,就开始执行fetch以下的代码,这是个有副作用的代码,一直到执行完成,最后会调用action.cb(data, store.dispatch),实际上就是在调用传过来的回调。

最后这回调会回调另一个名称onInitData的Action Creator函数,作最后在reducer上的更动,再更动到store上。

redux-thunk简化了这个自订middleware的来回处理过程,它直接用Action Creator函数中声明时就是一个函数类型的action,到middleware就会执行,除非不是函数类型的action才交往下一个middleware。代码简单并不代表概念就简单,反而很多实作的细节看官方文件会清楚些。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题