redux中间件函数

本文详细探讨了Redux中间件的原理,通过实例展示了如何记录操作类型action和状态state,以及如何利用Monkeypatching重写dispatch方法。通过封装dispatch并使用多个Monkeypatching,实现了日志记录和错误报告功能。最后,文章讨论了中间件的改进,包括使用箭头函数简化代码,并调整applyMiddlewareByMonkeypatching辅助函数,确保中间件的正确链接和调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

middleware中间件函数

中间件本质就是得到一个新的dispatch创建函数,调用创建函数,重写了store.dispatch(action),依次执行新的dispatch(action)方法,来改变store中的state
我们从一个小案例来说明中间件函数的由来:

我们需要记录一下操作类型action和下一个状态state

//由action创建函数addTodo创建一个action对象
const action = addTodo('Use Redux')
//打印action对象
console.log('dispatching', action)
store.dispatch(action)
//打印下一个状态
console.log('next state', store.getState())

这是我们想要的效果,但不想每次都这样做手动创建,我们希望用一个函数来自动创建

封装dispatch函数

将日志记录提取到函数中:


function dispatch(store, action) {
  console.log('dispatching', action)
  store.dispatch(action)
  console.log('next state', store.getState())
}

然后,在任何地方使用它,而不是store.dispatch():

dispatch(store, addTodo('Use Redux'))

我们可以到这里结束,但是每次都导入一个特殊的函数不是很方便。

使用 Monkeypatching (猴子补丁)重写dispatch方法

Monkeypatching 是一种黑客攻击。“替换任何你喜欢的方法”

const next = store.dispatch  //保存源dispatch
//重写dispatch
store.dispatch = function dispatch(action) {
  console.log('dispatching', action)
  let result = next(action)   //调用原始的dispatch方法
  console.log('next state', store.getState())
  return result
}

同样,我们我们也使用一个函数封装一下,这样我们就不用每次都是编写一遍,这样就是一个中间件雏形了

function logMiddileware(store) {
const next = store.dispatch  //保存源dispatch
//重写dispatch
store.dispatch = function dispatch(action) {
  console.log('dispatching', action)
   //调用原始的dispatch方法,相当于store.dispatch(action)
   //store.dispatch(action)返回结果是当前action对象
  let result = next(action)  
  console.log('next state', store.getState())
  return result
}
}

这已经更接近我们想要的了!无论我们在哪里调度一个动作,它都保证被记录。但总感觉哪里不对

使用多个 Monkeypatching (猴子补丁)重写dispatch方法

增加一个错误报告

function logMiddileware(store) {
  const next = store.dispatch
  store.dispatch = function dispatch(action) {
    console.log('dispatching', action)
    let result = next(action)
    console.log('next state', store.getState())
    return result
  }
}

function errorMiddileware(store) {
  const next = store.dispatch
  store.dispatch = function dispatch(action) {
    try {
      return next(action)
    } catch (err) {
      console.error('Caught an exception!', err)
      Raven.captureException(err, {
        extra: {
          action,
          state: store.getState()
        }
      })
      throw err
    }
  }
}

logMiddileware(store)
errorMiddileware(store)

不过,这并不好,感觉怪怪的,我们使用dispatch传递的参数是action,这里传递的是store,就让人有些疑惑,所以我们希望返回一个封装后的dispatch函数。

function logMiddileware(store) {
  const next = store.dispatch
  // 之前是直接将store.dispatch重写
  // store.dispatch = function dispatch(action) {}

  return function (action) {
    console.log('dispatching', action)
    let result = next(action)
    console.log('next state', store.getState())
    return result
  }
}
let  dispatch=store.dispatch(store)
	 dispatch(action)   

但是我们调用logger函数是还是要传递store对象,不是我们想要的,所以我们使用一个辅助函数来实现我们想要的store.dispatch(action)

//我们在这里传递store
function applyMiddlewareByMonkeypatching(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()
  
  //在这里重写了store.dispatch
  middlewares.forEach(middleware => (store.dispatch = middleware(store)))
}

这样我们就可以直接调用store.dispatch(action)
但是

 middlewares.forEach(middleware => (store.dispatch = middleware(store)))

store.dispatch(action)还是被重写了,
为什么我们还是要在applyMiddlewareByMonkeypatching中重写store.dispatch,这样目的是使每个中间件都能调用store.dispatch,这样中间件都被链接起来了,但是,它仍然是猴子补丁,我们要想办法解决。

修改中间件

function logMiddileware(store) {
//返回一个dispatch创建函数,这样我们就可以接收store.dispatch当做参数
  return function dispatchCreator(dispatch) {
    return function dispatch(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}

使用箭头函数更加精简

const logMiddileware = store => dispatch => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

我们通常会把dispatch函数用next来命名

const logMiddileware = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

修改applyMiddlewareByMonkeypatching辅助函数

我们把applyMiddlewareByMonkeypatching辅助函数修改一下,变成下面这个样子:

function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()
  let dispatch = store.dispatch

  middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)))
  return Object.assign({}, store, { dispatch })
}

注意这里,我们先声明了变量dispatch,然后将store.dispatch赋值给他,这样dispatch 是当做参数被传递进去,没有被重写

  let dispatch = store.dispatch
 middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)))
  • 第一次传递的dispatch是原始的store.dispatch
  • middleware(store)返回dispatch创建函数dispatchCreator,再调用创建函数dispatchCreator(dispatch),然后在返回dispatch函数,每次循环的时候他会把上一个dispatch函数传给下一个中间件函数,依次从外到内传递下去,直到最后一个。
  • 最后一个中间件运行的会调用传递的dispatch(action),然后又依次从内到外运行dispatch(action)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值