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

Redux核心概念

Redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API。
Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。(功能有限 需要中间件)
Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

Redux中间件

dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
换言之,中间件都是对store.dispatch()的增强。
不难发现:

  1. 不使用middleware时,在dispatch(action)时会执行rootReducer,并根据action的type更新返回相应的state。
  2. 而在使用middleware时,简言之,middleware会将我们当前的action做相应的处理,随后再交付rootReducer执行。
    https://cn.redux.js.org 中文官方文档
    阮一峰

    注意,不太需要研究如何写中间件,因为:常用的中间件都有现成的,只要引用别人写好的模块即可。
    故 只要按照中间件官方提供的规定格式 实现程序,并应用到中间件方法,那么,在执行一个action的时候,就会成功调用中间件。

中间件 的软件工程定义是什么

中间件不是产出最终结果,而是 处理一些 程序流程中间 的操作。
作用:中间件常做一些 业务逻辑之间 的数据转换,让开发者不需要关注底层逻辑,而专心于当前的开发。
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(乱的数据)==>service转换(中间件)==>UI(整齐的数据)

示例:使用redux中间件

applymiddleware将一堆函数封装成一个函数,这些函数的执行顺序由next传递。

import { createStore, applyMiddleware } from 'redux';

// 中间件为 高阶函数:1.函数做参数 2.return出的也只是函数
const logger1 = store => next => action => { //柯里化:next函数 在store的源码中被一番处理后返回(高阶函数),新next再接收参数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为一个函数,否则报错
const reducer = (state, action) => state;
const store = createStore(reducer, {}, enhancer); //{}为初始化的store数据
store.dispatch({type:1});
store.dispatch({type:2});
store.dispatch({type:3});
//createStore第3个参数enhancer若存在,将先执行enhancer内的中间件逻辑后再dispatch,而后才执行reducer的逻辑。
//可见中间件增强enhance了reducer&createStore

详解JS函数柯里化 - 简书
柯里化:多参函数->单参函数
就是把一个由多个参数的函数,写成 多个函数嵌套,每个函数只有1个参数,每个函数返回下一个函数。

redux不用中间件 的代码书写顺序
action ==> dispatch ==> reducer ==> nextState

redux用中间件 的代码书写顺序 (middleware是在dispatch和reducer之间起作用)
action ==> middleware ==> dispatch ==> reducer ==> nextState

如何自己写一个中间件

在官方的示例中,有一个logger实现的示例

const logger = store => next => action =>{
    console.log('prev state',store.getState()) //获取状态数据
    console.log('dispatch',action);

    let result = next(action);

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

    return result;
}

从这个示例可以看出,实现一个中间件需要做一下几件事情:

  1. 三层函数包装:第一层传入store,第二层传入下一个中间件next,第三层传入此次执行的action;
  2. 在最内层的函数实现中,需要调用next中间件,并返回结果。
    参考 https://www.jianshu.com/p/dbe65d95c77b

applyMiddleware与redux中间件的原理

封装改造store.dispatch,将其指向中间件,以实现 在dispatch和reducer之间 处理action数据的逻辑。
大体原理如下

// 储存原来的dispatch
const dispatch = store.dispatch;

// 改变dispatch指向
store.dispatch = dispatchPre; //指向中间件

// 重命名
const next = dispatch;

参考 源码分析 https://segmentfault.com/a/1190000018347235

redux-thunk中间件+applymiddleware 实现 能异步的reducer

Redux store 默认仅支持同步数据流。 使用 thunk中间件 可以帮助在 Redux 应用中实现 异步的reducer。比如,action中 有setTimeout、ajax/fetch调用远程API 这些场景,就可用thunk中间件。
可以将 thunk中间件 看做 store 的 dispatch() 方法的封装器。我们可以使用 thunk的"action creator结构函数"来派遣 函数或Promise,而不是返回 action 对象,,具体如下。
背景: 没有 thunk 的话,默认地是同步派遣。虽然,同步机制 依然可以从React组件发出API调用(例如使用componentDidMount()生命周期方法发出这些请求),但在Redux应用中,同步派遣 难以实现以下两点:1.可重用性(思考下合成) 2.可预测性,只有 action creator 可以是状态更新的单一数据源。

直接将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函数

其实applyMiddleware就是Redux的一个原生方法,将所有中间件组成一个数组,依次执行。
中间件多了可以当做参数依次传进applyMiddleware。

Redux-thunk可以让reducer直接使用 异步操作,通过把 异步的action creator 放入reducer中。
如下就是个action creator,它必须返回一个函数(而非对象),可以是异步函数

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

addCountAsync函数就返回了一个函数,将dispatch作为函数的第一个参数传递进去,在函数内进行异步操作就可以了。

该部分参考如下
redux-thunk的源码分析和更多操作 见
Redux中间件之redux-thunk使用详解 - 简书 源码分析
Redux-thunk中间件 - 简书 使用方法 如何写异步ajax的fetch+promise接收数据

全文参考

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279

推荐阅读更多精彩内容