Skip to content

Latest commit

 

History

History
141 lines (88 loc) · 5.73 KB

05.Middleware (part 1).md

File metadata and controls

141 lines (88 loc) · 5.73 KB

Middleware

在Redux中,还有一个很重要的概念就是中间件,下面就看看Redux中的中间件是什么?帮我们解决了什么问题?

为什么引入Middleware

在前面几篇介绍中,所有的Action都是同步的行为,也就是说当dispatch一个Action之后,Store中的state会被立即更新。

但是,在现实的应用场景中,很多Action都是异步的,最常见的就是对于服务端的异步访问。

为了模拟一个异步行为,这里对前面章节的任务管理应用的例子进行简单的修改,模拟一个异步的添加任务的行为:

function asyncAddTask(name, category) {
    return function(dispatch) {
        setTimeout(function() {
            dispatch({
                type: ADD_TASK,
                name,
                category
            });
        }, 2000);
    }
}

当运行修改后的代码时,会遇到下面的错误:

Error: Actions must be plain objects. Use custom middleware for async actions.    

错误信息中给了比较详细的提示,Action必须是plain object,对于异步action需要使用中间件。

关于Middleware

一般来说,中间件都有链式组合特性,可以对数据进行流式处理

简单的说,在一些情况下,我们需要通过中间件帮我们进行特定的数据处理,从而得到我们想要的数据:

A -----> B

A ---> middleware 1 ---> middleware 2 ---> middleware 3 --> ... ---> B

这里就是对中间件的一个简单的认识,下面看看Redux中的中间件。

Redux中的Middleware

在Redux中,Middlerwares主要的作用就是处理Action。

Redux中的Action必须是一个plain object。但是为了实现异步的Action或其他功能,这个Action可能就是一个函数,或者是一个promise对象。这是就需要中间件帮助来处理这种特殊的Action了。

也就是说,Redux中的Middleware会对特定类型action做一定的转换,所以最后传给reducer的action一定是标准的plain object

针对Action的特征,Reudx Middleware可以采取不同的操作:

  • 可以选择传递给下一个中间件,如:next(action)
  • 可以选择跳过某些中间件,如:dispatch(action)
  • 或者更直接了当的结束传递,如:return。

Redux中常用的中间件:

  • redux-thunk:action可以是一个函数,用来发起异步请求。
  • redux-promise:action可以是一个promise对象,用来更优雅的进行异步操作。
  • redux-logger:action就是一个标准的plain object,用来记录action和nextState的。

Redux Middleware模式

在Redux中,Middleware就是严格遵守下面模式的函数:

var someMiddleware = function ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            // your middleware-specific code goes here
        }
    }
}

关于这个Middleware的模式函数,是一个三层嵌套的函数组成:

  1. 第一层函数暴露一个store API的子集给middleware(dispatch函数和getState函数),这两个参数可以给内层函数提供触发action和获取state的能力

  2. 第二层函数提供了中间件的链式组合能力,通过next,我们可以显式的把该中间件处理过的输入传给下一个中间件

  3. 第三层函数支持从上一个中间件或者dispatch传递过来的action参数,在函数内部经可以结合该action来确定下一步操作了

    • 可以选择传递给下一个中间件,如:next(action)
    • 可以选择跳过某些中间件,如:dispatch(action)
    • 可以结束传递,如:return

下面回到如何处理异步行为,Redux中通常使用redux-thunk来处理异步的行为。

找到redux-thunk的源码如下(下面的是修改后版本),就简简单单的几行,严格遵守了Middleware pattern:

var thunkMiddleware = function({ dispatch, getState }) {
    // console.log('Enter thunkMiddleware');
    return function(next) {
        // console.log('Function "next" provided:', next);
        return function (action) {
            // console.log('Handling action:', action);
            // 如果action是一个函数,就是执行action(dispatch, getState)
            return typeof action === 'function' ?
                action(dispatch, getState) :
                next(action)
        }
    }
}    

demo

有了thunkMiddleware的支持之后,就可以支持异步的asyncAddTask了,通过下面的命名运行异步action的代码:

npm run example "demo/05.01.asyncAction.js"

Action的处理变成了下面的数据流:

asyncAddTask(action creator) ---> action ---> dispatcher ---> thunkMiddleware ---> reducers    

代码的执行结果如下,异步的action可以正常运行。

注意由于action是异步执行的,所以为了能够得到正常的结果,一定要等到action执行完毕之后再对store进行unsubscribe:

initial state is: { category: 'All', tasks: [] }

-> SELECT_CATEGORY Action
State change to: { category: 'Reading', tasks: [] }

-> ADD_TASK Action
State change to: { category: 'Reading',
  tasks: [ { name: 'Write a blog', category: 'Writing' } ] }

-> ADD_TASK Action
State change to: { category: 'Reading',
  tasks:
   [ { name: 'Write a blog', category: 'Writing' },
     { name: 'Read ES6 spec', category: 'Reading' } ] }

run unsubscribe after async action complete