看完此篇文章你能夠了解到: 原文連接react
redux 的核心思想爲:將須要修改的 state 都存入到 store 裏,發起一個 action 用來描述發生了什麼,用 reducers 描述 action 如何改變 state tree 。建立 store 的時候須要傳入 reducer,真正能改變 store 中數據的是 store.dispatch API。git
中間件是 dispatch 一個 action 到觸發 reducer 之間作的一個額外操做,一般使用中間件 Middleware 來進行日誌記錄、建立崩潰報告、調用異步接口、路由、或者改變dispatch
;github
import { createStore, applyMiddleware, combineReducers } from "redux";
import rootReducer from "./reducers/index";
import thunk from "redux-thunk";
const store = createStore(
combineReducers({ ...rootReducer }),
applyMiddleware([thunk])
);
複製代碼
此處使用了異步 action 中間件 thunk,沒錯就是傳入給 applyMiddleware 便可完成 dispatch 的加強。那麼有兩個問題?redux
當有多箇中間件時,每個 middleware 是如何操做前一箇中間件包裝過的 dispatch?
如何編寫本身的中間件?
applyMiddleware 便可回答第 2 個問題,applyMiddleware 函數接受一箇中間件數組,並依次執行中間件,將上一個 middleware 包裝過的 store.dispatch 傳遞給下一個中間件。數組
//一個簡單的 applyMiddleware 實現(非官方的 API,後面會介紹)
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice();
middlewares.reverse(); //爲什麼要反序?
/** 因爲是依次執行中間件,那麼當前中間件執行完成確定得執行下一個中間件,作到鏈式調用; 之因此將列表反序的目的是爲了在遍歷的時候,讓上一個中間件知道下一個中間件的dispatch是什麼;(可能這裏有點繞,下面講述Redux API的時候會介紹) **/
let dispatch = store.dispatch;
middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)));
return Object.assign({}, store, { dispatch });
}
//提早透露:一個簡單的中間件,每個中間件中須要有當前的store和下一個dispatch。
const logger = store => next => action => {
console.log("dispatching", action);
let result = next(action); //next爲下一個dispatch;
console.log("next state", store.getState());
return result;
};
複製代碼
理解:app
中間件的執行是順序執行的
,爲了可以鏈式執行中間件,須要在每個中間件中知道下一個 dispatch,這樣就能夠跳轉到下一個中間件;異步
每一箇中間件的dispatch生成實際上是反序的
,由於 A 在調用時須要知道 B 的 dispatch,B 在執行時須要知道 C 的 dispatch,那麼須要先知道 C 的 dispatch。(下面 Redux API 源碼會驗證這點)async
在每個中間件中,都是可使用 next 函數(也就是下一個的 dispatch 函數);函數
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args); //取到當前的store
let dispatch = () => {
throw new Error(
"Dispatching while constructing your middleware is not allowed. " +
"Other middleware would not be applied to this dispatch."
);
};
const middlewareAPI = {
//每一個 middleware 接受 Store 的 dispatch 和 getState 函數做爲命名參數
getState: store.getState, //返回應用當前的 state 樹。
dispatch: (...args) => dispatch(...args)
};
// 依次調用每個中間件
const chain = middlewares.map(middleware => middleware(middlewareAPI));
//如今 此處的chain應該是一個函數數組[],一個相似於
/** [ function(next){ return function(action){ } }, function(next){ return function(action){ } } ] **/
//compose(...functions)從右到左來組合多個函數
//做用:compose(funcA, funcB, funcC) 形象爲 compose(funcA(funcB(funcC())));
// 其效果相似於上一部分講述的在循環中獲得上一個dispatch
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
複製代碼
理解:fetch
上面簡單例子講到,dispatch的生成實際上是反序的
能夠從 compose 中看出端倪:compose(funcA, funcB, funcC) 形象爲 compose(funcA(funcB(funcC())));
看 conmpose 源碼其實你會發現,最後 compose(...chain)的結果應該爲:
function(){
funcA(funcB(funcC()))
}
複製代碼
因此在執行compose(...chain)(store.dispatch)
的時候,內部其實先調用了 funcC 來生成 C 的 dispatch。
此處可能有點超前,若是您不知道如何編寫中間件請先閱讀下一節,再回到這裏來看
//第一個中間件
function createMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
console.log("第一個的下一個的dispatch", next);
console.log("第一個action", action);
const result = next(action);
console.log("第一個state", getState());
return result;
};
}
const firstMid = createMiddleware();
export default firstMid;
複製代碼
//第二個中間件
function createMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
console.log("第二個的下一個的dispatch", next);
console.log("第二個action", action);
const result = next(action);
console.log("第二個state", getState());
return result;
};
}
const secondMid = createMiddleware();
export default secondMid;
複製代碼
//中間件使用
const middlewares = [firstMid, secondMid]; //注意中間件的順序
const store = createStore(
combineReducers({ ...rootReducer }),
composeWithDevTools(applyMiddleware(...middlewares))
);
//實際打印的結果
/** 第一個的下一個的dispatch ƒ (action) { console.log('第二個的下一個的dispatch', next); console.log('第二個action', action); var result = next(action); console.log('第二個state', getState()); return resu… 第一個action {type: "GLOBAL_DATA", globalData: {…}} 第二個的下一個的dispatch ƒ dispatch(action) { if (!isPlainObject(action)) { throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); } if (typeof action.type === 'unde… 第二個action {type: "GLOBAL_DATA", globalData: {…}} 第二個state {routing: {…}, global: {…}, home: {…}} 第一個state {routing: {…}, global: {…}, home: {…}} **/
複製代碼
格式爲:
function yourMiddleware() {
return ({ getState, dispatch }) => next => action => {};
}
複製代碼
...middlewares (arguments): 遵循 Redux middleware API 的函數。每一個 middleware 接受 Store 的 dispatch 和 getState 函數做爲命名參數,並返回一個函數。該函數會被傳入 被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接收 action 的新函數;
這個函數能夠直接調用 next(action),或者在其餘須要的時刻調用,甚至根本不去調用它。調用鏈中最後一個 middleware 會接受真實的 store 的 dispatch 方法做爲 next 參數,並藉此結束調用鏈。因此,middleware 的函數簽名是 ({ getState, dispatch }) => next => action。
一個記錄日誌的中間件:
function createLoggerkMiddleware() {
return ({ dispatch, getState }) => next => action => {
console.log("will dispatch", action);
// 調用 middleware 鏈中下一個 middleware 的 dispatch。
let returnValue = next(action);
console.log("state after dispatch", getState());
// 通常會是 action 自己,除非
// 後面的 middleware 修改了它。
return returnValue;
};
}
const logger = createLoggerkMiddleware();
export default logger;
複製代碼
理解:
const middlewares = [thunk, middleware];
const store = createStore(
combineReducers({ ...rootReducer }),
composeWithDevTools(applyMiddleware(...middlewares))
);
//action中
//這是一個同步action
const receiveInfo = response => ({
type: 'RECEIVE_HOME',
homeInfo: response
});
//使用redux-thunk異步執行action
export const getInfo = () => async (dispatch, getState) => {
try {
const response = await new Promise((resolve, reject) => {
/* 模擬異步操做成功,這樣能夠經過fetch調接口獲取數據 */
setTimeout(() => {
resolve({ title: 'React App' });
}, 1000);
});
await dispatch(receiveInfo (response));//使用dispatch觸發同步action
return response;
} catch (error) {
console.log('error: ', error);
return error;
}
};
//在react中
let {getInfo}=this.props;
getInfo().then({
})
複製代碼
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
//能夠接受一個返回函數的action creator。若是這個action creator 返回的是一個函數,就將dispatch的決策權交給此函數,若是不是,就按照原來的next(action)執行。
if (typeof action === "function") {
return action(dispatch, getState, extraArgument);//這就是上面例子的函數爲啥接受dispatch和getState兩個參數的緣由
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
複製代碼