redux异步action_React躬行记(12)——Redux中间件
Redux的中間件(Middleware)遵循了即插即用的設(shè)計思想,出現(xiàn)在Action到達(dá)Reducer之前(如圖10所示)的位置。中間件是一個固定模式的獨(dú)立函數(shù),當(dāng)把多個中間件像管道那樣串聯(lián)在一起時,前一個中間件不但能將其輸出傳給下一個中間件作為輸入,還能中斷整條管道。在引入中間件后,既能擴(kuò)展Redux的功能,也能增強(qiáng)dispatch()函數(shù),適應(yīng)不同的業(yè)務(wù)需求,例如通過中間件記錄日志、報告奔潰或處理異步請求等。
圖10 中間件管道一、開發(fā)模式
在設(shè)計中間件函數(shù)時,會遵循一個固定的模式,如下代碼所示,使用了柯里化、高階函數(shù)等函數(shù)式編程中的概念。
function middleware(store) {return function(next) {return function(action) {return next(action);};}; }middleware()函數(shù)接收一個Store實(shí)例,返回值是一個接收next參數(shù)的函數(shù)。其中next也是一個函數(shù),用來將控制權(quán)轉(zhuǎn)移給下一個中間件,從而實(shí)現(xiàn)了中間件之間的串聯(lián),它會返回一個處理Action對象的函數(shù)。由于閉包的作用,在這最內(nèi)層的函數(shù)中,依然能調(diào)用外層的對象和函數(shù),例如訪問action所攜帶的數(shù)據(jù)、執(zhí)行store中的dispatch()或getState()方法等。示例中的middleware()函數(shù)只是單純的將接收到的action對象轉(zhuǎn)交給后面的中間件,沒有對其做額外的處理。
之所以將中間件函數(shù)寫成層層嵌套的模式,有以下幾個原因。
(1)擴(kuò)展Redux需要遵循函數(shù)式編程的設(shè)計思想。
(2)柯里化讓中間件更容易處理數(shù)據(jù)流,即便于形成數(shù)據(jù)處理管道。
(3)每個中間件的職責(zé)單一(即函數(shù)代碼盡量少),通過嵌套組合完成復(fù)雜功能。
利用ES6中的箭頭函數(shù)能將middleware()函數(shù)改寫得更加簡潔,如下所示。
const middleware = store => next => action => {return next(action); };二、applyMiddleware()
Redux提供了組織中間件的applyMiddleware()函數(shù),在閱讀過此函數(shù)的源碼(如下所示)之后,才能更好的理解中間件的開發(fā)模式。
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)源碼分析
接下來會分析函數(shù)中的代碼,為了便于理解,在說明中還會給出相應(yīng)的語句。
(1)利用剩余參數(shù)(...middlewares)的方式,applyMiddleware()函數(shù)可以接收任意多個中間件。
(2)返回一個接收createStore參數(shù)的函數(shù),在函數(shù)體中調(diào)用Redux的createStore()函數(shù),得到一個store實(shí)例。
const store = createStore(...args);(3)middlewareAPI變量包含了中間件需要的參數(shù),即store.getState()和dispatch()方法。
const middlewareAPI = {getState: store.getState,dispatch: (...args) => dispatch(...args) };(4)將middlewareAPI變量傳遞給每個中間件并調(diào)用一次,再將返回的函數(shù)組成一個新數(shù)組。
const chain = middlewares.map(middleware => middleware(middlewareAPI));(5)通過compose()增強(qiáng)dispatch()方法,compose()是Redux內(nèi)置的函數(shù),可從右向左合成多個函數(shù),例如compose(f1, f2, f3)(arg)相當(dāng)于f1(f2(f3(arg)))。
dispatch = compose(...chain)(store.dispatch);(6)最終得到一個對象,包含store實(shí)例的方法和增強(qiáng)后的dispatch()方法。
return { ...store, dispatch };2)使用方式
applyMiddleware()函數(shù)有兩種使用方式,下面用一個例子來演示,先定義兩個中間件:m1和m2,以及一個Reducer函數(shù):caculate()。
const m1 = store => next => action => {console.log("m1");return next(action); }; const m2 = store => next => action => {console.log("m2");return next(action); }; function caculate(previousState = {digit:0}, action) {let state = Object.assign({}, previousState);switch (action.type) {case "ADD":state.digit += 1;break;case "MINUS":state.digit -= 1;}return state; }(1)applyMiddleware()是一個三級柯里化的函數(shù),如果要使用,那么可以像下面這樣調(diào)用,先傳兩個中間件,再傳createStore()函數(shù),最后傳caculate()函數(shù)。
let store = applyMiddleware(m1, m2)(createStore)(caculate);(2)applyMiddleware()函數(shù)還可以作為createStore()的最后一個參數(shù),如下代碼所示,第一個參數(shù)是caculate()函數(shù),第二個參數(shù)是applyMiddleware()函數(shù)的結(jié)果。
let store = createStore(caculate, applyMiddleware(m1, m2));在applyMiddleware()函數(shù)的內(nèi)部,chain數(shù)組的值是[m1(next), m2(next)],由m1和m2返回的包含next參數(shù)的函數(shù)組成。增強(qiáng)后的dispatch()方法通過m1(m2(store.dispatch))得到,具體如下所示。
dispatch = action => {console.log("m1");return next(action); };當(dāng)調(diào)用store實(shí)例的dispatch()方法(如下代碼所示)時,會先輸出“m1”;然后調(diào)用next()函數(shù),也就是m2(next),輸出“m2”;最后調(diào)用m2中的next()函數(shù),也就是dispatch()方法。注意,不能在中間件中直接調(diào)用dispatch()方法,以免造成死循環(huán)。
store.dispatch({ type: "ADD" });三、redux-thunk
如果要在Redux中處理異步請求,那么可以借助中間件實(shí)現(xiàn),目前市面上已有很多封裝好的中間件可供使用,例如redux-thunk、redux-promise或redux-saga等。本節(jié)將著重講解redux-thunk中間件,其核心代碼如下所示。
function createThunkMiddleware(extraArgument) {return ({ dispatch, getState }) => next => action => {if (typeof action === "function") {return action(dispatch, getState, extraArgument);}return next(action);}; }首先檢測action的類型,如果是函數(shù),那么就直接調(diào)用并將dispatch、getState和extraArgument作為參數(shù)傳入;否則就調(diào)用next參數(shù),轉(zhuǎn)移控制權(quán)。redux-thunk其實(shí)擴(kuò)展了dispatch()方法,使其參數(shù)既可以是JavaScript對象,也可以是函數(shù)。
接下來用一個簡單的例子演示redux-thunk的用法,如下代碼所示,首先通過import引入redux-thunk,并定義一個異步Action。
import thunk from "redux-thunk"; function asynAction() {return dispatch => {fetch("server.php").then(response => response.json(),error => console.log(error)).then(data => {dispatch(data); //{type: "ADD"}});}; }然后讓asynAction()函數(shù)返回一個接收dispatch參數(shù)的函數(shù),在函數(shù)體內(nèi)通過fetch()函數(shù)請求服務(wù)端的資源,再利用得到的Promise處理獲取到的數(shù)據(jù)。在第二個then()方法中,data參數(shù)被賦為{type: "ADD"},也就是server.php響應(yīng)的數(shù)據(jù),其代碼如下所示。
<?php $json = ['type' => 'ADD' ]; echo json_encode($json);再接入redux-thunk中間件,即將thunk傳給applyMiddleware()函數(shù),最后發(fā)送一個異步Action,如下所示。
let store = createStore(caculate, applyMiddleware(thunk)); store.dispatch(asynAction());總結(jié)
以上是生活随笔為你收集整理的redux异步action_React躬行记(12)——Redux中间件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 修改telnet提示并非_俊翔:修改EC
- 下一篇: 郑州智慧岛大数据管委会_数据科学融通应用