Redux: 中间件坏了

创建于 2018-06-06  ·  3评论  ·  资料来源: reduxjs/redux

漏洞

一个重要的中间件模式已被打破。 IMO 大多数中间件解决方案封装了他们自己的关注点,并且独立于其他中间件的存在。 最近进行了一项更改,以打破仅对特定中间件子集造成小问题的模式:那些在applyMiddleware初始化路径期间与其他中间件交互时表现出不同行为的模式。

这里有些例子:

// Make state aware of browser media queries
const mediaQueries = (mediaQueries = defaultMediaQueries): Middleware => store => {
  if (typeof window !== 'undefined') {
    const reverseMap = createReverseMap(mediaQueries)

    const handleMediaChange = ({media, matches}: MediaChangedActionPayload) =>
      store.dispatch(mediaChanged(reverseMap[media], matches))

    const addMatchListener = (media: string) => {
      const match = window.matchMedia(media)
      match.addListener(handleMediaChange)
      return match.matches
    }

    values(mediaQueries).forEach(media => 
      handleMediaChange({media, matches: addMatchListener(media)}))
  }

  return next => (action: any) => next(action)
}

另一个例子:

// Make state aware of user adblockers
const adBlockDetection: Middleware = store => {
  if (typeof document !== 'undefined' && !document.getElementById('XMpADSwgbPUC')) {
    store.dispatch({type: AD_BLOCKER_DETECTED, payload: true})
  }
  return next => (action: any) => next(action)
}

另一个例子:

// Make state aware of socket connectivity and allow synchronisation of actions
const socketMiddleware = ({actionCreator, url, dataSync}) => store => {
  const socket = new WebSocket(url.replace(/(http|https)/, 'ws'))

  socket.addEventListener('message', ({data}) => {
    store.dispatch(JSON.parse(data))
  })

  socket.addEventListener('open', () => {
    socket.send(syncCreator(CONNECT_TO_DOMAIN, store.getState(), null, socket))
    store.dispatch(actionCreator({connected: true}))
  })

  socket.addEventListener('close', reason => {
    store.dispatch(actionCreator({connected: false}))
  })

  return next => action => {
    if (dataSync.hasOwnProperty(action.type)) {
      socket.send(syncCreator(action.type, store.getState(), action.payload, socket))
    }
    return next(action)
  }
}

目前的行为是什么?

applyMiddleware期间抛出以下错误,实质上破坏了应用程序:

Uncaught Error: Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch

重构上面的中间件以被next =>函数关闭(如下所示)没有区别(但显然也不是一个可行的解决方案)。

// Still broken:
const mediaQueries = (mediaQueries = defaultMediaQueries): Middleware => store => next => {
  // ...middlewareCodeGoesHere...
  return (action: any) => next(action)
}

什么是预期行为?

不要破坏应用程序。 如果需要,请记录警告,这样我就可以忽略它,因为在我 100% 的用例中_我不在乎其他中间件是否可以处理该调度_。 我强烈怀疑有(或应该是)很少有正当理由说明从中间件上下文中的任何分派都应该与堆栈中的任何其他中间件进行交互,如果确实如此,我会认为代码异味(它会放置至少对中间件的隐式顺序依赖)。

哪些版本的 Redux、哪些浏览器和操作系统受此问题影响?

一直工作到这个 PR

最有用的评论

@markerikson是的,我见过这个问题。 新设计“解决”的关键问题是:

问题是:当您应用中间件时,该侦听器会触发,因此调用store.dispatch 就是调用非中间件调度,我们的分析中间件将看不到任何事件

这是 IMO,应用程序开发人员应该处理的实现细节,而不是应该_破坏大多数中间件的工作方式_。 通过警告而不是抛出错误,应用程序开发人员可以识别问题,并重构他们自己的中间件来解决它。

在当前状态下,所有现有的中间件,包括 3rd 方中间件,使用这种内部调度模式(基本上是任何需要在任何东西上实例化侦听器的东西)都被破坏了。

不幸的是,继续使用 3.x 不是我们的选择,因为我们使用 Typescript,并且只有 4.x 具有正确的类型,并且一般来说,我们宁愿不使用我们的核心部分的旧版本应用程序逻辑,即使打字不是问题。

在我看来,即使是错误消息本身也暗示了它的真正重要性:“问题”是Other middleware would not be applied to this dispatch ,实际上很少有人需要,到目前为止,它只是一个特定的问题用例,其中分析中间件取决于从路由器分派的操作。

重要的是要注意,redux 中的“修复”现在也破坏了它打算修复的代码。 它破坏了路由器中间件。 现在,该中间件(以及所有其他类似中间件)的作者需要发布其库的 4.x 兼容版本,现在需要公开消费者必须实现的手动调度的“存储就绪”操作的实现细节。

中间件库的使用已经从简单地导入包,然后插入到中间件数组中,现在还需要应用程序作者找到一些hacky 方法来在商店创建完成后的某个时间分派任意的“商店准备好”动作,但是在它被使用之前。 现在他们所有的中间件都必须有希望遵守相同的操作类型,否则应用程序作者将需要分派多个“商店就绪”操作,不同的类型匹配中间件作者实现的任何 API。

这真是一团糟,在这种情况下,“治愈”显然比“疾病”糟糕得多。

所有3条评论

中间件初始化过程在 4.0 中您链接的 PR 中进行了明确更改。 这是一个深思熟虑的设计决定,旨在解决 #1240。

除了尝试更改中间件的逻辑以等待一些手动调度的“商店准备就绪”操作之外,我不确定我是否对您的用例有直接的建议。 您也可以考虑继续使用 3.x。

@markerikson是的,我见过这个问题。 新设计“解决”的关键问题是:

问题是:当您应用中间件时,该侦听器会触发,因此调用store.dispatch 就是调用非中间件调度,我们的分析中间件将看不到任何事件

这是 IMO,应用程序开发人员应该处理的实现细节,而不是应该_破坏大多数中间件的工作方式_。 通过警告而不是抛出错误,应用程序开发人员可以识别问题,并重构他们自己的中间件来解决它。

在当前状态下,所有现有的中间件,包括 3rd 方中间件,使用这种内部调度模式(基本上是任何需要在任何东西上实例化侦听器的东西)都被破坏了。

不幸的是,继续使用 3.x 不是我们的选择,因为我们使用 Typescript,并且只有 4.x 具有正确的类型,并且一般来说,我们宁愿不使用我们的核心部分的旧版本应用程序逻辑,即使打字不是问题。

在我看来,即使是错误消息本身也暗示了它的真正重要性:“问题”是Other middleware would not be applied to this dispatch ,实际上很少有人需要,到目前为止,它只是一个特定的问题用例,其中分析中间件取决于从路由器分派的操作。

重要的是要注意,redux 中的“修复”现在也破坏了它打算修复的代码。 它破坏了路由器中间件。 现在,该中间件(以及所有其他类似中间件)的作者需要发布其库的 4.x 兼容版本,现在需要公开消费者必须实现的手动调度的“存储就绪”操作的实现细节。

中间件库的使用已经从简单地导入包,然后插入到中间件数组中,现在还需要应用程序作者找到一些hacky 方法来在商店创建完成后的某个时间分派任意的“商店准备好”动作,但是在它被使用之前。 现在他们所有的中间件都必须有希望遵守相同的操作类型,否则应用程序作者将需要分派多个“商店就绪”操作,不同的类型匹配中间件作者实现的任何 API。

这真是一团糟,在这种情况下,“治愈”显然比“疾病”糟糕得多。

仍然可以使您的每个示例都有效。 只需在 applyMiddleware 之后调度。 也许在某种setupMiddleware(store)函数或商店增强器中。

Error 是故意引入的,因为替代方案是允许用户遇到原因非常不明显的边缘情况(除非您熟悉applyMiddleware的内部结构)。 Redux 采取尽可能防止用户错误的立场,所以这不会消失。

一种替代方法可能是某种回调,中间件可以将其注册为一种afterApply事件。 然后,您将保证在正确的时间进行调度,在一切都按顺序应用之后。 仔细想想会很有趣。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

mickeyreiss-visor picture mickeyreiss-visor  ·  3评论

elado picture elado  ·  3评论

timdorr picture timdorr  ·  3评论

dmitry-zaets picture dmitry-zaets  ·  3评论

ms88privat picture ms88privat  ·  3评论