zoukankan      html  css  js  c++  java
  • React躬行记(12)——Redux中间件

      Redux的中间件(Middleware)遵循了即插即用的设计思想,出现在Action到达Reducer之前(如图10所示)的位置。中间件是一个固定模式的独立函数,当把多个中间件像管道那样串联在一起时,前一个中间件不但能将其输出传给下一个中间件作为输入,还能中断整条管道。在引入中间件后,既能扩展Redux的功能,也能增强dispatch()函数,适应不同的业务需求,例如通过中间件记录日志、报告奔溃或处理异步请求等。

    图10  中间件管道

    一、开发模式

      在设计中间件函数时,会遵循一个固定的模式,如下代码所示,使用了柯里化、高阶函数等函数式编程中的概念。

    function middleware(store) {
      return function(next) {
        return function(action) {
          return next(action);
        };
      };
    }

      middleware()函数接收一个Store实例,返回值是一个接收next参数的函数。其中next也是一个函数,用来将控制权转移给下一个中间件,从而实现了中间件之间的串联,它会返回一个处理Action对象的函数。由于闭包的作用,在这最内层的函数中,依然能调用外层的对象和函数,例如访问action所携带的数据、执行store中的dispatch()或getState()方法等。示例中的middleware()函数只是单纯的将接收到的action对象转交给后面的中间件,没有对其做额外的处理。

      之所以将中间件函数写成层层嵌套的模式,有以下几个原因。

    (1)扩展Redux需要遵循函数式编程的设计思想。

    (2)柯里化让中间件更容易处理数据流,即便于形成数据处理管道。

    (3)每个中间件的职责单一(即函数代码尽量少),通过嵌套组合完成复杂功能。

      利用ES6中的箭头函数能将middleware()函数改写得更加简洁,如下所示。

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

    二、applyMiddleware()

      Redux提供了组织中间件的applyMiddleware()函数,在阅读过此函数的源码(如下所示)之后,才能更好的理解中间件的开发模式。

    function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        const store = createStore(...args);
        let dispatch = () => {
          throw new Error(
            "Dispatching while constructing your middleware is not allowed. " +
              "Other middleware would not be applied to this dispatch."
          );
        };
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        };
        const chain = middlewares.map(middleware => middleware(middlewareAPI));
        dispatch = compose(...chain)(store.dispatch);
        return { ...store, dispatch };
      };
    }

    1)源码分析

      接下来会分析函数中的代码,为了便于理解,在说明中还会给出相应的语句。

      (1)利用剩余参数(...middlewares)的方式,applyMiddleware()函数可以接收任意多个中间件。

      (2)返回一个接收createStore参数的函数,在函数体中调用Redux的createStore()函数,得到一个store实例。

    const store = createStore(...args);

      (3)middlewareAPI变量包含了中间件需要的参数,即store.getState()和dispatch()方法。

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    };

      (4)将middlewareAPI变量传递给每个中间件并调用一次,再将返回的函数组成一个新数组。

    const chain = middlewares.map(middleware => middleware(middlewareAPI));

      (5)通过compose()增强dispatch()方法,compose()是Redux内置的函数,可从右向左合成多个函数,例如compose(f1, f2, f3)(arg)相当于f1(f2(f3(arg)))。

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

      (6)最终得到一个对象,包含store实例的方法和增强后的dispatch()方法。

    return { ...store, dispatch };

    2)使用方式

      applyMiddleware()函数有两种使用方式,下面用一个例子来演示,先定义两个中间件:m1和m2,以及一个Reducer函数:caculate()。

    const m1 = store => next => action => {
      console.log("m1");
      return next(action);
    };
    const m2 = store => next => action => {
      console.log("m2");
      return next(action);
    };
    function caculate(previousState = {digit:0}, action) {
      let state = Object.assign({}, previousState);
      switch (action.type) {
        case "ADD":
          state.digit += 1;
          break;
        case "MINUS":
          state.digit -= 1;
      }
      return state;
    }

      (1)applyMiddleware()是一个三级柯里化的函数,如果要使用,那么可以像下面这样调用,先传两个中间件,再传createStore()函数,最后传caculate()函数。

    let store = applyMiddleware(m1, m2)(createStore)(caculate);

      (2)applyMiddleware()函数还可以作为createStore()的最后一个参数,如下代码所示,第一个参数是caculate()函数,第二个参数是applyMiddleware()函数的结果。

    let store = createStore(caculate, applyMiddleware(m1, m2));

      在applyMiddleware()函数的内部,chain数组的值是[m1(next), m2(next)],由m1和m2返回的包含next参数的函数组成。增强后的dispatch()方法通过m1(m2(store.dispatch))得到,具体如下所示。

    dispatch = action => {
      console.log("m1");
      return next(action);
    };

      当调用store实例的dispatch()方法(如下代码所示)时,会先输出“m1”;然后调用next()函数,也就是m2(next),输出“m2”;最后调用m2中的next()函数,也就是dispatch()方法。注意,不能在中间件中直接调用dispatch()方法,以免造成死循环。

    store.dispatch({ type: "ADD" });

    三、redux-thunk

      如果要在Redux中处理异步请求,那么可以借助中间件实现,目前市面上已有很多封装好的中间件可供使用,例如redux-thunk、redux-promise或redux-saga等。本节将着重讲解redux-thunk中间件,其核心代码如下所示。

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

      首先检测action的类型,如果是函数,那么就直接调用并将dispatch、getState和extraArgument作为参数传入;否则就调用next参数,转移控制权。redux-thunk其实扩展了dispatch()方法,使其参数既可以是JavaScript对象,也可以是函数。

      接下来用一个简单的例子演示redux-thunk的用法,如下代码所示,首先通过import引入redux-thunk,并定义一个异步Action。

    import thunk from "redux-thunk";
    function asynAction() {
      return dispatch => {
        fetch("server.php").then(
          response => response.json(),
          error => console.log(error)
        ).then(data => {
          dispatch(data);        //{type: "ADD"}
        });
      };
    }

      然后让asynAction()函数返回一个接收dispatch参数的函数,在函数体内通过fetch()函数请求服务端的资源,再利用得到的Promise处理获取到的数据。在第二个then()方法中,data参数被赋为{type: "ADD"},也就是server.php响应的数据,其代码如下所示。

    <?php
    $json = [
      'type' => 'ADD'
    ];
    echo json_encode($json);

      再接入redux-thunk中间件,即将thunk传给applyMiddleware()函数,最后发送一个异步Action,如下所示。

    let store = createStore(caculate, applyMiddleware(thunk));
    store.dispatch(asynAction());
  • 相关阅读:
    1026 Table Tennis (30)
    1029 Median
    1025 PAT Ranking (25)
    1017 Queueing at Bank (25)
    1014 Waiting in Line (30)
    1057 Stack (30)
    1010 Radix (25)
    1008 Elevator (20)
    字母大小写转换
    Nmap的基础知识
  • 原文地址:https://www.cnblogs.com/strick/p/10808591.html
Copyright © 2011-2022 走看看