一文全解Redux中間件middleware & applymiddleware 柯里化 redux thunk

Redux核心概念

Redux的核心概念其實(shí)很簡(jiǎn)單:將需要修改的state都存入到store里,發(fā)起一個(gè)action用來(lái)描述發(fā)生了什么,用reducers描述action如何改變state tree 。創(chuàng)建store的時(shí)候需要傳入reducer,真正能改變store中數(shù)據(jù)的是store.dispatch API。
Reducer:純函數(shù),只承擔(dān)計(jì)算 State 的功能,不合適承擔(dān)其他功能,也承擔(dān)不了,因?yàn)槔碚撋?,純函?shù)不能進(jìn)行讀寫操作。(功能有限 需要中間件)
Action:存放數(shù)據(jù)的對(duì)象,即消息的載體,只能被別人操作,自己不能進(jìn)行任何操作。

Redux中間件

dispatch一個(gè)action之后,到達(dá)reducer之前,進(jìn)行一些額外的操作,就需要用到middleware。 你可以利用 Redux middleware 來(lái)進(jìn)行日志記錄、創(chuàng)建崩潰報(bào)告、調(diào)用異步接口或者路由等等。
換言之,中間件都是對(duì)store.dispatch()的增強(qiáng)。
不難發(fā)現(xiàn):

  1. 不使用middleware時(shí),在dispatch(action)時(shí)會(huì)執(zhí)行rootReducer,并根據(jù)action的type更新返回相應(yīng)的state。
  2. 而在使用middleware時(shí),簡(jiǎn)言之,middleware會(huì)將我們當(dāng)前的action做相應(yīng)的處理,隨后再交付rootReducer執(zhí)行。
    https://cn.redux.js.org 中文官方文檔
    阮一峰

    注意,不太需要研究如何寫中間件,因?yàn)椋?strong>常用的中間件都有現(xiàn)成的,只要引用別人寫好的模塊即可。
    故 只要按照中間件官方提供的規(guī)定格式 實(shí)現(xiàn)程序,并應(yīng)用到中間件方法,那么,在執(zhí)行一個(gè)action的時(shí)候,就會(huì)成功調(diào)用中間件。

中間件 的軟件工程定義是什么

中間件不是產(chǎn)出最終結(jié)果,而是 處理一些 程序流程中間 的操作。
作用:中間件常做一些 業(yè)務(wù)邏輯之間 的數(shù)據(jù)轉(zhuǎn)換,讓開發(fā)者不需要關(guān)注底層邏輯,而專心于當(dāng)前的開發(fā)。
Middleware makes it easier for software developers to implement communication and input/output, so they can focus on the specific purpose of their application. -- Wiki
比如 ajax==>json(亂的數(shù)據(jù))==>service轉(zhuǎn)換(中間件)==>UI(整齊的數(shù)據(jù))

示例:使用redux中間件

applymiddleware將一堆函數(shù)封裝成一個(gè)函數(shù),這些函數(shù)的執(zhí)行順序由next傳遞。

import { createStore, applyMiddleware } from 'redux';

// 中間件為 高階函數(shù):1.函數(shù)做參數(shù) 2.return出的也只是函數(shù)
const logger1 = store => next => action => { //柯里化:next函數(shù) 在store的源碼中被一番處理后返回(高階函數(shù)),新next再接收參數(shù)action
    console.log('current dipatch' + JSON.stringify(action));
    next(action);
};
const logger2 = store => next => action=> {
    next(action);
};
const logger3 = store => next => action=> {
    next(action);
};

const enhancer = applyMiddleware(logger1, logger2, logger3); //enhancer為一個(gè)函數(shù),否則報(bào)錯(cuò)
const reducer = (state, action) => state;
const store = createStore(reducer, {}, enhancer); //{}為初始化的store數(shù)據(jù)
store.dispatch({type:1});
store.dispatch({type:2});
store.dispatch({type:3});
//createStore第3個(gè)參數(shù)enhancer若存在,將先執(zhí)行enhancer內(nèi)的中間件邏輯后再dispatch,而后才執(zhí)行reducer的邏輯。
//可見中間件增強(qiáng)enhance了reducer&createStore

詳解JS函數(shù)柯里化 - 簡(jiǎn)書
柯里化:多參函數(shù)->單參函數(shù)
就是把一個(gè)由多個(gè)參數(shù)的函數(shù),寫成 多個(gè)函數(shù)嵌套,每個(gè)函數(shù)只有1個(gè)參數(shù),每個(gè)函數(shù)返回下一個(gè)函數(shù)。

redux不用中間件 的代碼書寫順序
action ==> dispatch ==> reducer ==> nextState

redux用中間件 的代碼書寫順序 (middleware是在dispatch和reducer之間起作用)
action ==> middleware ==> dispatch ==> reducer ==> nextState

如何自己寫一個(gè)中間件

在官方的示例中,有一個(gè)logger實(shí)現(xiàn)的示例

const logger = store => next => action =>{
    console.log('prev state',store.getState()) //獲取狀態(tài)數(shù)據(jù)
    console.log('dispatch',action);

    let result = next(action);

    console.log('next state',store.getState());

    return result;
}

從這個(gè)示例可以看出,實(shí)現(xiàn)一個(gè)中間件需要做一下幾件事情:

  1. 三層函數(shù)包裝:第一層傳入store,第二層傳入下一個(gè)中間件next,第三層傳入此次執(zhí)行的action;
  2. 在最內(nèi)層的函數(shù)實(shí)現(xiàn)中,需要調(diào)用next中間件,并返回結(jié)果。
    參考 http://www.itdecent.cn/p/dbe65d95c77b

applyMiddleware與redux中間件的原理

封裝改造store.dispatch,將其指向中間件,以實(shí)現(xiàn) 在dispatch和reducer之間 處理action數(shù)據(jù)的邏輯。
大體原理如下

// 儲(chǔ)存原來(lái)的dispatch
const dispatch = store.dispatch;

// 改變dispatch指向
store.dispatch = dispatchPre; //指向中間件

// 重命名
const next = dispatch;

參考 源碼分析 https://segmentfault.com/a/1190000018347235

redux-thunk中間件+applymiddleware 實(shí)現(xiàn) 能異步的reducer

Redux store 默認(rèn)僅支持同步數(shù)據(jù)流。 使用 thunk中間件 可以幫助在 Redux 應(yīng)用中實(shí)現(xiàn) 異步的reducer。比如,action中 有setTimeout、ajax/fetch調(diào)用遠(yuǎn)程API 這些場(chǎng)景,就可用thunk中間件。
可以將 thunk中間件 看做 store 的 dispatch() 方法的封裝器。我們可以使用 thunk的"action creator結(jié)構(gòu)函數(shù)"來(lái)派遣 函數(shù)或Promise,而不是返回 action 對(duì)象,,具體如下。
背景: 沒有 thunk 的話,默認(rèn)地是同步派遣。雖然,同步機(jī)制 依然可以從React組件發(fā)出API調(diào)用(例如使用componentDidMount()生命周期方法發(fā)出這些請(qǐng)求),但在Redux應(yīng)用中,同步派遣 難以實(shí)現(xiàn)以下兩點(diǎn):1.可重用性(思考下合成) 2.可預(yù)測(cè)性,只有 action creator 可以是狀態(tài)更新的單一數(shù)據(jù)源。

直接將thunk中間件引入,放在applyMiddleware方法之中,傳入createStore方法,就完成了store.dispatch()的功能增強(qiáng)。即可以在reducer中進(jìn)行一些異步的操作。

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import reeducers from '....';
const store = createStore(
  reducers, 
  applyMiddleware(thunk, logger) //thunk本身是一個(gè)中間件,logger也是一個(gè)中間件,還可以放更多
); //thunk的實(shí)例被傳遞給Redux的applyMiddleware() enhancer函數(shù)

其實(shí)applyMiddleware就是Redux的一個(gè)原生方法,將所有中間件組成一個(gè)數(shù)組,依次執(zhí)行。
中間件多了可以當(dāng)做參數(shù)依次傳進(jìn)applyMiddleware。

Redux-thunk可以讓reducer直接使用 異步操作,通過(guò)把 異步的action creator 放入reducer中。
如下就是個(gè)action creator,它必須返回一個(gè)函數(shù)(而非對(duì)象),可以是異步函數(shù)

export function addCount() {
  return {type: ADD_COUNT}
}
export function addCountAsync() { 
  return dispatch => {
    setTimeout( () => {
      dispatch(addCount())
    },2000)
  }
}

addCountAsync函數(shù)就返回了一個(gè)函數(shù),將dispatch作為函數(shù)的第一個(gè)參數(shù)傳遞進(jìn)去,在函數(shù)內(nèi)進(jìn)行異步操作就可以了。

該部分參考如下
redux-thunk的源碼分析和更多操作 見
Redux中間件之redux-thunk使用詳解 - 簡(jiǎn)書 源碼分析
Redux-thunk中間件 - 簡(jiǎn)書 使用方法 如何寫異步ajax的fetch+promise接收數(shù)據(jù)

全文參考

阮一峰 -- Redux 入門教程(二):中間件與異步操作 (涉及redux-thunk redux-promise) https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容