zoukankan      html  css  js  c++  java
  • redux之applyMiddleware

    redux之所以伟大就在于中间件了,中间件为redux提供了无限可能。redux中中间件是一个不太容易理解的概念,因为涉及到compose、hoc等函数式的概念,看源代码总是懵懵的感觉。今天我们就来详细解剖一下伟大的applyMiddleware吧。
    applyMiddleware只有短短三十多行,可见作者功力。先简单说下中间件是啥,在redux中,当你要dispatch一条命令给reducer时,预先定义的中间件会对这条命令进行各种转换,有的中间件会记录这个过程,有的会对payload进行转换,甚至可以终止这次dispatch(短路),再发起另一个命令。下面我们切入正题。

    入参

    export default applyMiddleware(...middlewares) {
        ...
    }
    

    首先通过rest参数的方式获取一个中间件数组。

    返回值

    export default applyMiddleware(...middlewares) {
        return (createStore) => (reducer, preloadedState, enhancer) => {
            
            return {
                ...store,
                dispatch
            }
        }
    }
    

    可以看到返回值是一个HOC,它接受一个createStore函数,因此你还可以使用applyMiddleware来创建store

    const finalCreateStore = applyMiddleware(thunk, log)(createStore)
    const store = finalCreateStore(reducer, preloadedState, enhancer)
    

    重点来了

    先把代码贴上,

    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []
    
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    

    这里不对compose做过多解释了,请大家自行了解。

    我们直接锁定到chain = middlewares.map(middleware => middleware(middlewareAPI)),这里每个middleware接受middlewareAPI作为参数,那么我们知道了middleware大概的样子即,

    const middleware = function({getState : function, dispatch: function}) {
    ...
    }
    

    我们继续,下一行代码就是精髓了,dispatch = compose(...chain)(store.dispatch),首先compose(...chain)不难理解,即从右到左chain中的每个middleware(注意这里的middleware并不是一开始传进来的middleware了,是一个函数执行的结果)所执行的结果将作为左边middleware的入参,后面以此类推,那么store.dispatch理所当然成为chain中最后一个middleware的入参,那么我们知道最后一个middleware应该是这样的。

    const middleware = function({getState : function, dispatch: function}) {
          return (store.dispatch) {
            ...
          } 
     }
    

    这里我们换一个更加普适的写法,

    const middleware = function({getState : function, dispatch: function}) {
          return (next) => {
            ...
          } 
     }
    

    难点来了,到这里显而易见的线索就不多了,由代码可以看出,最终这个compose后的dispatch被return出去了,我们在实际开发中,比如dispatch({type: 'INCREMENT'}),这里的dispatch并不是store.dispatch(记为realDispatch),而是return的那个dispatch(我们记为fakeDispatch),因此又多了不少线索,我们fakeDispatch的action最终要传给realDispatch来触发reducer中的逻辑,那么我们如何传递action呢? 我们回到我们现在猜测的middleware代码中,最后一个middleware的next就是realDispatch,现在我们把注意力放在这里,即应该要给左边的middleware返回什么呢,

    // 这是middleware的执行结果
    const middleare = (next) => {
        // 这里要返回什么呢
    }
    

    如果我们返回一个非函数,那么这个middleware以后就没啥事了,显然不行,因为我还需要接受action,再调用realDispatch;所以我们需要返回一个闭包函数,这个函数保留了next的访问引用,那么我们继续猜测最后一个middleware,

    const middleware = (next) => {
        return (action) => {
            next(action)
        }
    }
    

    OK,现在我们可以保证其可以将action顺利送给reducer了,回顾下compose,我们可以看到,(action) => {//...} 就是每个函数的返回值,它会作为入参传给左侧函数,这个入参即next,这样action从fakeDispatch出发,经过这样一个个action作为参数的next函数,最终到达realDispatch这个next并触发reducer执行。下面我们来个完整的middleware,

    const middleware = (getState, dispatch) => (next) => (action) => { // 具体逻辑...}
    

    如果你对redux-logger和redux-thunk的源码有些记忆的话你会很熟悉这个函数签名了吧。原来这个难懂的HOC就是这么来的。这你也就知道了redux-thunk为何要放到最左边,因为它要能够短路,即action从fakeDispatch传进来时,它是第一个接收的,这样它就可以根据anction类型来确定是否终止这个dispatch,下面我们写下简版redux-logger和redux-thunk,实战下。

    redux-thunk

    const middleware = (getState, dispatch) => (next) => (action) => {
        if(typeof action === 'function') {
            action(getState, dispatch)  // 短路
        } else {
            next(action)
        }
    }
    

    redux-logger

    const middleware = (getState, dispatch) => (next) => (action) => {
            console.log('开始触发reducer')
            next(action)
            console.log('结束触发reducer')
    }
    
  • 相关阅读:
    SQL中的全局变量和局部变量(@@/@)
    C# 委托Delegate(一) 基础介绍&用法
    internal in C#
    用代码块在new对象时set属性
    MySql与对应的Java的时间类型
    快速获取当天0点0分0秒(00:00:00)
    IsNullOrWhiteSpace与IsNullOrEmpty
    svn服务器配置 for mac
    CornerStone配置SVN,HTTP及SVN简单使用说明
    svn配置
  • 原文地址:https://www.cnblogs.com/shineyao/p/7635302.html
Copyright © 2011-2022 走看看