Redux中的middleware其实就像是给你提供一个在action发出到实际reducer执行之前处理一些事情的机会。可以允许我们添加自己的逻辑在这段当中。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。

加入middleware后,整个数据的流动如下图所示:

举个简单的例子,我们使用middleware将每次action的执行详细信息都打出来。就用官方demo中的todoApp来举例,我们先实现一个简单的reducer用来添加一个todo:

  1. const todoApp = (state = {todo: []}, action) => {
  2. if (action.type === ‘addTodo’) {
  3. state.todo = […state.todo, action.value]
  4. }
  5. return state
  6. }

然后再补上其他逻辑测试,用最原始的方法实现将每次action的执行信息log出来:

  1. const redux = require(‘redux’)
  2. const todoApp = (state = {todo: []}, action) => {
  3. if (action.type === ‘addTodo’) {
  4. state.todo = […state.todo, action.value]
  5. }
  6. return state
  7. }
  8. let store = redux.createStore(todoApp);
  9. const action = {
  10. type: ‘addTodo’,
  11. value: ‘todo’,
  12. }
  13. console.log(‘state: ‘, store.getState());
  14. console.log(‘action: ‘, action);
  15. store.dispatch(action)
  16. console.log(‘next state: ‘, store.getState())

如果不出什么意外的话,我们这段代码应该会成功运行,并且将这段log出这个action的运行情况, 如下图:

接下来我们将log这件事尝试使用redux的middleware来完成。

首先,根据我们之前的了解,middleware其实是一段在action到reducer之间的处理逻辑。我们都知道,标准的一个redux发送一个action是调用store自身的dispatch方法。那么,我们想要在一个action到达reducer之前去做些处理的话,最好的地方应该就是尝试将store的dispatch替换为我们自己的,在其中加上我们的处理逻辑,例如打印log这件事。

  1. let store = redux.createStore(todoApp);
  2. const next = store.dispatch;
  3. const dispatchWithLog = (action) => {
  4. console.log(‘state: ‘, store.getState());
  5. console.log(‘action: ‘, action);
  6. next(action);
  7. console.log(‘next state: ‘, store.getState());
  8. }
  9. store.dispatch = dispatchWithLog;

我们将默认store的dispatch替换为自己的dispatchWithLog, 通过这种方式,完成了我们的需求,只要任何地方调用了store的diapatch去发送新的action, 我们都能讲其log出来,这个看起来已经有一点middleware的意思了。

虽然上面已经可以解决问题了,并且已经有点middleware的意思了,但是还有一点硬伤就是,需求多了就比较难搞了,例如就像官方上既需要log又需要Crash Reporting, 再通过这种方式去处理就显得很不优雅。Crash Reporting的middle的一个简单实现如下:

  1. const next1 = store.dispatch;
  2. const dispatchWithCreshReporting = (action) => {
  3. try {
  4. next1(action);
  5. } catch (err) {
  6. console.error(err);
  7. }
  8. }
  9. store.dispatch = dispatchWithCreshReporting;

当然,redux本身给我们提供了包装过后的工具方法来专门应用middleware。其中也不是简单粗暴的替换store上的dispatch了。这个方法即为applyMiddleware。

官方的doc也给出了一个关于applyMiddleware的一个简单粗暴的直接替换dispatch的一个示例,如下:

  1. function applyMiddlewareByMonkeypatching(store, middlewares) {
  2. middlewares = middlewares.slice()
  3. middlewares.reverse()
  4. // Transform dispatch function with each middleware.
  5. middlewares.forEach(middleware =>
  6. store.dispatch = middleware(store)
  7. )
  8. }

关于先逆序middlewares再进行替换,这里主要是为了,让middleware的执行顺序按照我么传给他的array顺序来进行。就像我们上面直接替换的那个例子,越往后面进行替换dispatch的在执行过程中先运行。

当然,官放的具体实现中不是这么简单粗暴的直接替换的方式,因为一来不够优雅,这种方式在链式的调用过程中有可能出现问题。比如某一个middleware并不是同步执行的,这样在进行store.dispatch = middleware(store)就有可能到下一个middleware时,store.dispatch还没有被替换。因此,官方的middleware是接受一个next的参数来,来拿到dispatch,并不是直接从store上对dispatch进行操作的。

一般一个标准的middleware是这个样子的,我们使用最初的log的那个middleware来举例,让它接受一个next(就是一个下一个的dispatch方法),再返回一个dispatch方法。

  1. function logger(store) {
  2. return function(next) {
  3. return function(action) {
  4. console.log(‘state: ‘, store.getState());
  5. console.log(‘action: ‘, action);
  6. let result = next(action);
  7. console.log(‘next state: ‘, store.getState());
  8. return result;
  9. }
  10. }
  11. }
  12. function creshReporting(store) {
  13. return function(next) {
  14. return function(action) {
  15. try {
  16. return next(action);
  17. } catch (err) {
  18. console.error(err);
  19. return next;
  20. }
  21. }
  22. }
  23. }

然后假设我们在apply时这样应用一下:

  1. function applyMiddleware(store, middlewares = [logger, crashReporting]) {
  2. middlewares = middlewares.slice()
  3. middlewares.reverse()
  4. let dispatch = store.dispatch
  5. middlewares.forEach(middleware =>
  6. dispatch = middleware(store)(dispatch)
  7. )
  8. return Object.assign({}, store, { dispatch })
  9. }

这样就能够进行优雅的链式调用了。并且用上ES6箭头函数后,这样写出来会更加的优雅:

  1. const logger = store => next => action => {
  2. console.log(‘state: ‘, store.getState());
  3. console.log(‘action: ‘, action);
  4. let result = next(action);
  5. console.log(‘next state: ‘, store.getState());
  6. return result;
  7. }

最后,其实redux middleware使用起来其实是非常的方便的,只需要记住applyMiddleware这个API即可。即const store = createStore(reducer, applyMiddleware(middlewares))

分享完毕,喜欢搭建可以喜欢蜘蛛表格小编的分享,以后还会给大家分享更多内容

如何学习理解Redux Middleware的更多相关文章

  1. redux middleware 的理解

    前言 这几天看了redux middleware的运用与实现原理,写了一个百度搜索的demo,实现了类似redux-thunk和redux-logger中间件的功能. 项目地址:https://git ...

  2. 理解Redux以及如何在项目中的使用

    今天我们来聊聊Redux,这篇文章是一个进阶的文章,建议大家先对redux的基础有一定的了解,在这里给大家推荐一下阮一峰老师的文章: http://www.ruanyifeng.com/blog/20 ...

  3. 3.3 理解 Redux 中间件(转)

    这一小节会讲解 redux 中间件的原理,为下一节讲解 redux 异步 action 做铺垫,主要内容为: Redux 中间件是什么 使用 Redux 中间件 logger 中间件结构分析 appl ...

  4. 理解 Redux 中间件机制

    Redux 的 action 是一个 JS 对象,它表明了如何对 store 进行修改.但是 Redux 的中间件机制使action creator 不光可以返回 action 对象,也可以返回 ac ...

  5. 全面学习理解TLB(Translation Look-aside Buffer)地址变换高速缓存

    全面学习理解TLB(Translation Look-aside Buffer)地址变换高速缓存 前言: 本文学习思路是:存在缘由   --> 存在好处 --> 定义性质 --> 具 ...

  6. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  7. MLT的学习理解

    MLT的学习理解 MLT是一个开源的多媒体库,我们的音视频编辑工具,是使用它作为底层支持,某司的'快剪辑'pc版和安卓版,也是用的它. MLT简介 它的GitHub地址,这个库比较老了,现在只有一个作 ...

  8. 菜鸟之路——机器学习之SVM分类器学习理解以及Python实现

    SVM分类器里面的东西好多呀,碾压前两个.怪不得称之为深度学习出现之前表现最好的算法. 今天学到的也应该只是冰山一角,懂了SVM的一些原理.还得继续深入学习理解呢. 一些关键词: 超平面(hyper ...

  9. batch normalization学习理解笔记

    batch normalization学习理解笔记 最近在Andrew Ng课程中学到了Batch Normalization相关内容,通过查阅资料和原始paper,基本上弄懂了一些算法的细节部分,现 ...

随机推荐

  1. springBoot中“MockMvc”的进行Controller进行单元测试:application/octet-stream' not supported问题小结

    解决方案:这个问题其实是Content-type的问题,只需要在相关的代码加入相关Content-type中就可以了,代码如下: mockMvc.perform(post("/user&qu ...

  2. Linux 内核同步 urb

    不幸的是, 同步 urb 没有一个象中断, 控制, 和块 urb 的初始化函数. 因此它们必须在 驱动中"手动"初始化, 在它们可被提交给 USB 核心之前. 下面是一个如何正确初 ...

  3. promise 讲解

    Promise的出现  解决了 js 回调地狱得问题 回调地狱图 Promise解决回调地狱 是不是美观多了.. 实例化Promise时传入方法里的两个参数 resolve(成功的回调)和reject ...

  4. HDU 6444 Neko's loop(单调队列)

    Neko has a loop of size nn. The loop has a happy value aiai on the i−th(0≤i≤n−1)i−th(0≤i≤n−1) grid.  ...

  5. mac系统上访问docker容器中的ip配置

    使用 mac系统,发现docker没有 docker0网桥,无法直接在宿主机上 访问 容器的ip, 在测试的时候有这种需求,而不是通过-p的方式,可以参考下面的连接,主要就是 修改 setting.j ...

  6. 一目了然 | 数据库实例性能调优利器:Performance Insights

    Performance Insights是什么 阿里云RDS Performance Insights是RDS CloudDBA产品一项专注于用户数据库实例性能调优.负载监控和关联分析的利器,以简单直 ...

  7. Google 浏览器设置打开超链接到新窗口标签页

    一.windows  按住Ctrl + 鼠标点击,在新窗口打开,停留在当前页面: 按住Ctrl + Shift + 鼠标点击,在新窗口打开,停留在新窗口: 登录Google账号,管理Google账号, ...

  8. CentOS 7 端口白名单设置

    # 查看白名单列表 firewall-cmd --zone=public --list-ports # 添加白名单端口 firewall-cmd --zone=public --add-port=/t ...

  9. Java中的循环结构

    1.while循环结构 语法: while(循环条件){ //循环操作 } while循环结构流程图: 举例: int i = 1; while(i <= 100){ System.out.pr ...

  10. go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])

    目录 go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时]) 静态配置 flag注入 在线热加载配置 远程配置中心 go微 ...