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

Redux核心概念

Redux的核心概念其實很簡單:將需要修改的state都存入到store里憋他,發(fā)起一個action用來描述發(fā)生了什么,用reducers描述action如何改變state tree 亲铡。創(chuàng)建store的時候需要傳入reducer才写,真正能改變store中數(shù)據(jù)的是store.dispatch API。
Reducer:純函數(shù)奖蔓,只承擔計算 State 的功能赞草,不合適承擔其他功能,也承擔不了吆鹤,因為理論上厨疙,純函數(shù)不能進行讀寫操作。(功能有限 需要中間件)
Action:存放數(shù)據(jù)的對象疑务,即消息的載體沾凄,只能被別人操作梗醇,自己不能進行任何操作。

Redux中間件

dispatch一個action之后撒蟀,到達reducer之前叙谨,進行一些額外的操作,就需要用到middleware牙肝。 你可以利用 Redux middleware 來進行日志記錄唉俗、創(chuàng)建崩潰報告、調(diào)用異步接口或者路由等等配椭。
換言之虫溜,中間件都是對store.dispatch()的增強。
不難發(fā)現(xiàn):

  1. 不使用middleware時股缸,在dispatch(action)時會執(zhí)行rootReducer衡楞,并根據(jù)action的type更新返回相應的state。
  2. 而在使用middleware時敦姻,簡言之瘾境,middleware會將我們當前的action做相應的處理,隨后再交付rootReducer執(zhí)行镰惦。
    https://cn.redux.js.org 中文官方文檔
    阮一峰

    注意迷守,不太需要研究如何寫中間件,因為:常用的中間件都有現(xiàn)成的旺入,只要引用別人寫好的模塊即可兑凿。
    故 只要按照中間件官方提供的規(guī)定格式 實現(xiàn)程序,并應用到中間件方法茵瘾,那么礼华,在執(zhí)行一個action的時候,就會成功調(diào)用中間件拗秘。

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

中間件不是產(chǎn)出最終結果圣絮,而是 處理一些 程序流程中間 的操作。
作用:中間件常做一些 業(yè)務邏輯之間 的數(shù)據(jù)轉換雕旨,讓開發(fā)者不需要關注底層邏輯扮匠,而專心于當前的開發(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轉換(中間件)==>UI(整齊的數(shù)據(jù))

示例:使用redux中間件

applymiddleware將一堆函數(shù)封裝成一個函數(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為一個函數(shù),否則報錯
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個參數(shù)enhancer若存在突照,將先執(zhí)行enhancer內(nèi)的中間件邏輯后再dispatch帮非,而后才執(zhí)行reducer的邏輯。
//可見中間件增強enhance了reducer&createStore

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

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

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

如何自己寫一個中間件

在官方的示例中陨舱,有一個logger實現(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;
}

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

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

applyMiddleware與redux中間件的原理

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

// 儲存原來的dispatch
const dispatch = store.dispatch;

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

// 重命名
const next = dispatch;

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

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

Redux store 默認僅支持同步數(shù)據(jù)流。 使用 thunk中間件 可以幫助在 Redux 應用中實現(xiàn) 異步的reducer令哟。比如恼琼,action中 有setTimeout、ajax/fetch調(diào)用遠程API 這些場景屏富,就可用thunk中間件晴竞。
可以將 thunk中間件 看做 store 的 dispatch() 方法的封裝器。我們可以使用 thunk的"action creator結構函數(shù)"來派遣 函數(shù)或Promise狠半,而不是返回 action 對象,颓鲜,具體如下。
背景: 沒有 thunk 的話典予,默認地是同步派遣。雖然乐严,同步機制 依然可以從React組件發(fā)出API調(diào)用(例如使用componentDidMount()生命周期方法發(fā)出這些請求)瘤袖,但在Redux應用中,同步派遣 難以實現(xiàn)以下兩點:1.可重用性(思考下合成) 2.可預測性昂验,只有 action creator 可以是狀態(tài)更新的單一數(shù)據(jù)源捂敌。

直接將thunk中間件引入,放在applyMiddleware方法之中既琴,傳入createStore方法占婉,就完成了store.dispatch()的功能增強。即可以在reducer中進行一些異步的操作甫恩。

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

其實applyMiddleware就是Redux的一個原生方法,將所有中間件組成一個數(shù)組奖慌,依次執(zhí)行抛虫。
中間件多了可以當做參數(shù)依次傳進applyMiddleware。

Redux-thunk可以讓reducer直接使用 異步操作简僧,通過把 異步的action creator 放入reducer中建椰。
如下就是個action creator,它必須返回一個函數(shù)(而非對象)岛马,可以是異步函數(shù)

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

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

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

全文參考

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

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伞矩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹦浦,更是在濱河造成了極大的恐慌扭吁,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盲镶,死亡現(xiàn)場離奇詭異侥袜,居然都是意外死亡,警方通過查閱死者的電腦和手機溉贿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門枫吧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宇色,你說我怎么就攤上這事九杂。” “怎么了宣蠕?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵例隆,是天一觀的道長。 經(jīng)常有香客問我抢蚀,道長镀层,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任皿曲,我火速辦了婚禮唱逢,結果婚禮上,老公的妹妹穿的比我還像新娘屋休。我一直安慰自己坞古,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布劫樟。 她就那樣靜靜地躺著痪枫,像睡著了一般织堂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上听怕,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天捧挺,我揣著相機與錄音,去河邊找鬼尿瞭。 笑死闽烙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的声搁。 我是一名探鬼主播黑竞,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疏旨!你這毒婦竟也來了很魂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤檐涝,失蹤者是張志新(化名)和其女友劉穎遏匆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谁榜,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡幅聘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了窃植。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帝蒿。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖巷怜,靈堂內(nèi)的尸體忽然破棺而出葛超,到底是詐尸還是另有隱情,我是刑警寧澤延塑,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布绣张,位于F島的核電站,受9級特大地震影響关带,放射性物質(zhì)發(fā)生泄漏侥涵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一豫缨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧端朵,春花似錦好芭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春邻薯,著一層夾襖步出監(jiān)牢的瞬間裙戏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工厕诡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留累榜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓灵嫌,卻偏偏與公主長得像壹罚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寿羞,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容