Redux 进阶 -- 编写和使用中间件

本文目标:和你们探讨一下如何经过编写和使用 redex 中间件 来帮助咱们更好的使用 reduxnode

例子

在上一篇文章 Redux 进阶 -- 优雅的处理 async action 中,阿大经过改善流程对接完成了水果店的升级。git

可是阿大又有一个新的想法,他想详细的看看每个顾客的购买需求来了以后,帐本的先后变化。看来又要加一个新角色记录员了。难道要像加采购员那样手动的一个个的加吗?那可太麻烦了。正好阿大发现 redux 里有一个功能就是中间件。中间件是干吗的呢?简而言之,就是把顾客的需求从销售员收银员之间加上各类角色来处理。每个角色就是一个中间件。接下来阿大就开始来写中间件了。github

redux 中间件写起来其实很简单,就是一个函数而已。按照它的要求。这个函数接受一个 { dispatch, getState } 对象做为参数,而后返回一个 actionredux

那这样,就能够把原来的采购员也改形成中间件了,其实采购员就是拿到了顾客需求以后让顾客的需求延迟 dispatch,这用延迟用函数就能够作到了:app

// next 是用中间件加强以后的 dispatch
// dispatch 是最原始的 store.dispatch
const thunkMiddleware = ({ dispatch }) => next => action => {
  if (typeof action === 'function') {

    // 函数形式的 action 就把 dispatch 给这个 action,让 action 决定何时 dispatch (控制反转)
    return action(dispatch);
  }

  // 普通的 action 就直接传递给下一个中间件处理
  return next(action);
}
复制代码

而后咱们就须要把原来的顾客需求改一下了:async

// 买水果 - 进口苹果
function buyImportedApple(num) {

  // 返回一个函数类型的 action,这个函数接受 dispatch,能够决定何时 dispatch
  return dispatch => API.fetchImportedApple(() => dispatch({
    type: 'BUY_IMPORTED_APPLE',
    payload: num
  }));
}

// 买生鲜 - 进口鸡蛋
function buyImportedEgg(num) {
  return dispatch => API.fetchImportedEgg(() => dispatch({
    type: 'BUY_IMPORTED_EGG',
    payload: num
  }));
}
复制代码

而后采购员就能够只负责采购了,改回去:函数

// 采购商品生成器,不一样的商品须要不一样的时间采购
function fetching(time, callback) {

  // 用延时模拟采购时间
  const timer = setTimeout(() => {
    clearTimeout(timer);

    // 采购完成,通知销售员
    callback();
  }, time);
}

// 采购进口苹果须要 2 天(2s)
function fetchImportedApple(callback) {
  fetching(2000, callback);
}

// 采购进口苹果须要 3 天(3s)
function fetchImportedEgg(callback) {
  fetching(3000, callback);
}

// 采购员
const API = {
  fetchImportedApple, // 采购进口苹果
  fetchImportedEgg // 采购进口鸡蛋
}
复制代码

而后,咱们在添加一个记录员的中间件:源码分析

const loggerMiddleware = ({ getState }) => next => action => {
  console.log(`state before: ${JSON.stringify(getState())}`);
  console.log(`action: ${JSON.stringify(action)}`);
  const result = next(action);
  console.log(`state after: ${JSON.stringify(getState())}`);
  console.log('================================================');
  return result;
}
复制代码

删除掉原来的监听:post

- store.subscribe(() => console.log(JSON.stringify(store.getState())));
复制代码

好了,接下来就能够经过 reduxapplyMiddleware 来串联起这些中间件啦。fetch

const { createStore, combineReducers, applyMiddleware } = require('redux');

// 中间件的调用顺序是从右到左
const store = createStore(reducer, applyMiddleware(thunkMiddleware, loggerMiddleware));
复制代码

好了,大功告成,开始服务顾客:

store.dispatch(buyApple(3));
store.dispatch(buyImportedApple(10));
store.dispatch(buyEgg(1));
store.dispatch(buyImportedEgg(10));
store.dispatch(buyApple(4));
store.dispatch(buyImportedApple(10));
store.dispatch(buyEgg(8));
store.dispatch(buyImportedEgg(10));
// state before: {"fruit":{"apple":0,"importedApple":0},"fresh":{"egg":0,"importedEgg":0}}
// action: {"type":"BUY_APPLE","payload":3}
// state after: {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":0,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":0,"importedEgg":0}}
// action: {"type":"BUY_EGG","payload":1}
// state after: {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// action: {"type":"BUY_APPLE","payload":4}
// state after: {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// action: {"type":"BUY_EGG","payload":8}
// state after: {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":9,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":9,"importedEgg":0}}
// action: {"type":"BUY_IMPORTED_APPLE","payload":10}
// state after: {"fruit":{"apple":7,"importedApple":10},"fresh":{"egg":9,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":7,"importedApple":10},"fresh":{"egg":9,"importedEgg":0}}
// action: {"type":"BUY_IMPORTED_APPLE","payload":10}
// state after: {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":0}}
// ================================================
// state before: {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":0}}
// action: {"type":"BUY_IMPORTED_EGG","payload":10}
// state after: {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":10}}
// ================================================
// state before: {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":10}}
// action: {"type":"BUY_IMPORTED_EGG","payload":10}
// state after: {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":20}}
// ================================================
复制代码

上面咱们写的两个中间件其实就是 redux-thunkredux-logger 的简版。在实际中,推荐使用它们,会更可信。

讲解

编写 redux 中间件须要按照要求来,返回这样的函数

// 中间件接受一个对象,里面有原始的 dispatch,和 getState 方法用于获取 state
// 中间件函数返回一个函数,这个函数接受一个 next 参数,这个 next 是下一个中间件要作的事情 action => { ... }
function thunkMiddleware({ dispatch, getState }) {
  return function(next) {
    return function(action) {
      // 作你的事情
    }
  }
}
复制代码

图解

代码地址:Redux 进阶 -- 编写和使用中间件,直接控制台运行 node ./demo5/index.js 查看效果

上一篇:Redux 进阶 -- 优雅的处理 async action

下一篇:Redux 高级 -- 源码分析

相关文章
相关标签/搜索