前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何学习理解Redux Middleware

如何学习理解Redux Middleware

原创
作者头像
用户6820127
修改2020-01-08 18:44:58
3310
修改2020-01-08 18:44:58
举报
文章被收录于专栏:用户6820127的专栏

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))

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档