# Redux中间件原理与Redux-Thunk的原理

# 中间件原理

# 中间件写法

首先我们先看看怎么实现一个logger中间件

  • logger.js
export default store => next => action => {
  console.log('store:', store)
  next(action)
  console.log('action', action)
}
1
2
3
4
5

使用applyMiddleware(logger)加载中间件,从上面这个logger中间我们可以看到中间价是一个嵌套函数,三个参数分别是store,next,action

# 源码分析

  • node_modules/redux/src/applyMiddleware.js
export default 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

整个applyMiddleware返回一个函数

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

可以看出中间件的第一个参数是middlewareAPI

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

那么compose是干嘛的呢?

  • node_modules/redux/src/compose.js
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)))
}
1
2
3
4
5
6
7
8
9
10
11

可以看出compose是让函数嵌套执行的方法,那么上面compose就是吧store.dispatch经过一层层的封装,那么next就是原始的dispatch,action就是我们发送的action。
所以中间件的写法其实就是对dispatch进行了一层封装。

# Redux-Thunk原理

# Redux-Thunk使用

//action types
const GET_DATA = 'GET_DATA',
    GET_DATA_SUCCESS = 'GET_DATA_SUCCESS',
    GET_DATA_FAILED = 'GET_DATA_FAILED';
    
//action creator
const getDataAction = function(id) {
    return function(dispatch, getState) {
        dispatch({
            type: GET_DATA, 
            payload: id
        })
        api.getData(id) //注:本文所有示例的api.getData都返回promise对象
            .then(response => {
                dispatch({
                    type: GET_DATA_SUCCESS,
                    payload: response
                })
            })
            .catch(error => {
                dispatch({
                    type: GET_DATA_FAILED,
                    payload: error
                })
            }) 
    }
}

//reducer
const reducer = function(oldState, action) {
    switch(action.type) {
    case GET_DATA : 
        return oldState;
    case GET_DATA_SUCCESS : 
        return successState;
    case GET_DATA_FAILED : 
        return errorState;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# Redux-Thunk源码

  • node_modules/redux-thunk/lib/index.js
function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    return function (next) {
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }

        return next(action);
      };
    };
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

可以看thunk的实现十分简单,只是把dispatch作为参数传递给了action(此处action是个函数),而从上面的使用来看,在异步中按需dispatch新的action进行新的处理