Redux中间件和Store Enhancer

Redux中间件

redux的运作是属于同步进行的,但我们在处理数据接口的时候,往往需要异步的方式去完成,这个时候我们引用一些中间件。但在redux的流程中一般什么时候引用会比较合适呢?

redux的一般使用流程是dispatch -> action -> reducer -> store,中间件的使用在action与reducer之间是最合适的,所以使用时一般也是在这两者之间进行插入。

那一般怎么去区分从action后是直接进入到中间件中执行而不是到了reducer呢? 一般情况下中间件都是一个函数,所以判断dispatch返回的是函数还是对象来决定进入的中间件还是reducer中。

redux-thunk

redux-thunk是常用的处理异步的中间件,其源代码为:

function thunkMiddleware(extraArguments) {
  return ({dispatch, getState}) => (next) => (action) => {
    if(typeof action === 'function') {
      action(dispatch, getState, extraArgumanes);
    }
    // 抛给下一个中间件进行处理
    next(action);
  }
}

const thunk = thunkMiddleware();
export default thunk;

redux-thunk中源代码很简单,就是判断action的类型是否为function,如果是的则执行,不是则抛给下一个中间件进行处理。其中书写的方式采用了函数式编程的写法。

使用中间件

使用中间件我们引用了redux的applyMiddleware这个方法,具体的用法有两种:

// 方法一
import { createStore, applyMiddleware } from 'redux;
import thunkMiddleware from 'redux-thunk';

const configureStore = applyMiddleware(thunkMiddleware)(createStore);
const store = configureStore(reducer, initialState);

该方法是将thunkMiddleware作为参数,然后得到一个新的函数,得到的函数为一个store enhancer,然后将createStore作为一个参数获得一个增强版的store。但是该方式只能传一个中间件,对于使用多个中间件的方式不太适用。

// 方法二
import { createStore, applyMiddleware, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';

const win = window;
const storeEnhancer = compose(
  applyMiddleware(...middlewares),
  (win && win.devToolsExtension) ? win.devToolsExtension() : f => f;
);
const store = createStore(reducer, initialState, storeEnhancer);

方法二采用了compose的方式去整合多个中间件,而且applyMiddleware必须放在第一的位置,是为了能先让异步的中间件先处理,然后再处理其他的。createStore有三个参数,第一个代表着reducer,第二个则是默认的state,第三个参数则是增强器,用于增强store的功能。

自定义中间件

自定义中间件是同样需要遵循的方式为 retrun ({dispatch, getState?}) => (next) => (action) => next(action);

我们可以开发一个Promise的中间件

// 简单版
function isPromise(obj) {
  // 判断是否有then方法
  return obj && typeof obj.then === 'function';
}

export default function promiseMiddleware() {
  return ({dispatch}) => (next) => (action) => {
    return isPromise(action) ? action.then(dispatch) : next(action);
  }
}

// 增强版
export default function promiseMiddleware() {
  return ({dispatch}) => (next) => (action) => {
    const { types, promise. ...rest } = action;
    if(!isPromise(action) || !(types && types.length === 3)) {
      next(action);
    }

    const [ PENDING, DONE, FAIL ] = types;
    dispatch({...rest, type: PENDING});
    return action.promise.then(
      (result) => dispatch({...rest, result, type: DONE}),
      (error) => dispatch({...rest, error, type: FAIL})
    )
  }
}

所以在调用promiseMiddleware的时候需要传入的是一个对象
export const XX = (args) => {
return {
promise: …,
types: [FETCH_START, FETCH_SUCCESS, FETCH_FAIL]
}
}

Store Enhancer

中间件可以用来增强redux store的dispatch方法,但也仅限于dispatch方法。如果想要更深层次的增强,那么就需要使用store enhancer。

store enhancer主要用于增强store的功能,一个什么都不做的store enhancer如下:

const doNothingEnhancer = (createStore) => (reducer, proloadedState, enhancer) => {
  const store = createStore(reducer, proloadedState, enhancer);
  return store;
}

store enhancer的基本格式是传递一个createStore为参数,然后去改写原有的store。比如增加一个logEnhancer,代码如下:

const logEnhancer = (createStore) => (reducer, proloadedState, enhancer) => {
  const store = createStore(reducer, proloadedState, enhancer);

  const originalDispatch = store.dispatch;
  store.dispatch => (action) => {
    console.log('dispatch action: ' + action);
    originalDispatch(action);
  }

  return store;
}