本文目標:和你們探討一下如何經過編寫和使用
redex 中間件
來幫助咱們更好的使用redux
。node
在上一篇文章 Redux 進階 -- 優雅的處理 async action 中,阿大經過改善流程對接完成了水果店的升級。git
可是阿大又有一個新的想法,他想詳細的看看每個顧客的購買需求來了以後,帳本的先後變化。看來又要加一個新角色記錄員了。難道要像加採購員那樣手動的一個個的加嗎?那可太麻煩了。正好阿大發現 redux
裏有一個功能就是中間件。中間件是幹嗎的呢?簡而言之,就是把顧客的需求從銷售員到收銀員之間加上各類角色來處理。每個角色就是一箇中間件。接下來阿大就開始來寫中間件了。github
redux
中間件寫起來其實很簡單,就是一個函數而已。按照它的要求。這個函數接受一個 { dispatch, getState }
對象做爲參數,而後返回一個 action
。redux
那這樣,就能夠把原來的採購員也改形成中間件了,其實採購員就是拿到了顧客需求以後讓顧客的需求延遲 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())));
複製代碼
好了,接下來就能夠經過 redux
的 applyMiddleware
來串聯起這些中間件啦。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-thunk
和 redux-logger
的簡版。在實際中,推薦使用它們,會更可信。
編寫 redux 中間件須要按照要求來,返回這樣的函數
// 中間件接受一個對象,裏面有原始的 dispatch,和 getState 方法用於獲取 state
// 中間件函數返回一個函數,這個函數接受一個 next 參數,這個 next 是下一個中間件要作的事情 action => { ... }
function thunkMiddleware({ dispatch, getState }) {
return function(next) {
return function(action) {
// 作你的事情
}
}
}
複製代碼
代碼地址:Redux 進階 -- 編寫和使用中間件,直接控制檯運行
node ./demo5/index.js
查看效果
下一篇:Redux 高級 -- 源碼分析