React Flux - 在调度中调度 - 如何避免?

Posted

技术标签:

【中文标题】React Flux - 在调度中调度 - 如何避免?【英文标题】:React Flux - dispatching within a dispatch - how to avoid? 【发布时间】:2016-04-22 02:21:46 【问题描述】:

我似乎遇到了在 Flux 中无法避免 dispatch-within-a-dispatch 问题的情况。

我已经阅读了一些关于这个问题的类似问题,但除了setTimeout hacks 之外似乎没有一个很好的解决方案,我想避免这些问题。

我实际上使用的是alt.js 而不是 Flux,但我认为这些概念是相同的。

场景

想象一个最初呈现登录表单的组件。当用户登录时,这会触发 XHR,该 XHR 最终会以身份验证信息(例如用户名)进行响应,然后根据身份验证信息获取一些安全数据并呈现它而不是登录表单。

我遇到的问题是,当我尝试触发一个动作以根据 XHR 响应获取数据时,它仍然处于 LOGIN_RESPONSE 动作的调度中,并触发了可怕的

Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

示例

我创建了this jsfiddle 来演示这个问题。

我有一个Wrapper 组件,它根据用户是否设置在MyStore 中来呈现登录按钮或Contents 子组件。

    首先在Wrapper组件中渲染登录按钮。 单击按钮会调度LOGIN 操作。 延迟后,将调度 LOGIN_RESPONSE 操作(通过 async mechanism in alt.js)。 此操作触发MyStore 更新用户名。 Wrapper 组件观察存储变化并更新其状态。 这会导致Wrapper 呈现Content 组件而不是登录按钮。 Content 组件在挂载时尝试调度 FETCH_DATA 操作,但由于调度程序仍在调度 LOGIN_RESPONSE 而失败。 (如果我将 FETCH_DATA 调度包在 setTimeout 中,它可以工作,但感觉就像是 hack)。

这似乎是一种常见的情况。事实上,几乎所有相关问题都有类似的场景,但没有好的或具体的答案。

React - Authentication process : Cannot dispatch in the middle of a dispatch Dispatching cascading/dependent async requests in Flux/React Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch

此数据流是否存在本质上的问题?做这种事情的正确 Flux 方式是什么?

【问题讨论】:

【参考方案1】:

我相信我们忠实的朋友调度员有权投诉。

在得出结论之前,我将尝试描述一个假设的情况。假设一个应用程序有两个商店 S1 和 S2 以及两种操作 A1 和 A2。通常 Flux 实现的正确流程应该是这样的:

    组件触发一个动作 A1(基本上是一个调度); 单个调度程序将对应的有效负载分发到所有已注册的商店; S1 使用负载并可能更新其状态; 在 S1 中监听更改的所有组件都会检查它们感兴趣的更改,并可能更新其内部状态(可能会触发重新渲染); S2 消耗 ...(如第 3 步) 监听 S2 中的变化的所有组件...(如第 4 步) 现在所有存储都已完成对动作负载的处理,组件可以触发新动作(A1 或 A2)。

与传统 MVC 相比,使用 Flux 的最大优势之一是 Flux 为您提供可预测性的礼物。这种感觉使开发人员相信,通过正确应用 Flux 理念,他们确信执行顺序总是有点类似于:

A1 > S1 > S2 > A2 > S1 > S2 > ...

这真的很棒,尤其是在尝试查找错误来源时。有人可能会争辩说,执行可预测的事件链可能会导致效率低下,他可能是对的,尤其是在处理异步调用时,但这是你为拥有如此强大的权力付出的代价!

由于异步调用,事情可能会变得有些混乱。可能会发生以下一系列事件:

A1 > S1 > A2 > S2 > S1 > S2 > ...

也许您的应用可以很好地处理此类事件链,但这种“不可预测性”损害了 Flux 单向数据流背后的基本动机。

我觉得社区对于如何处理这种情况没有达成共识,但我将分享我的一般解决方案:“为了可预测性,请确保在您完全处理完最后一个操作之前不要触发任何新操作”。

执行此操作的一种方法是下载应用程序在触发任何其他操作之前成功重新渲染所需的所有必要(新)数据。在您的示例中,这可以通过首先下载 LOGIN_RESPONSE 和 FETCH_DATA 操作中涉及的数据并将其包装在单个有效负载中然后调度它来实现,因此所有组件都将在存储中拥有他们想要的数据而无需请求更多.

【讨论】:

【参考方案2】:

这是许多库中componentDidMount 中调度的常见问题。解决方案是将调度包装在 React 的批量更新中;幸运的是,Alt 允许您使用 batchingFunction 选项执行此操作:

var alt = new Alt(
  // React.addons.batchedUpdates is deprecated:
  // batchingFunction: React.addons.batchedUpdates

  // use this instead in newer versions of React
  // see https://discuss.reactjs.org/t/any-plan-for-reactdom-unstable-batchedupdates/1978
  batchingFunction: ReactDOM.unstable_batchedUpdates
);

有关工作示例,请参阅 https://jsfiddle.net/BinaryMuse/qftyfjgy/,有关不同框架中相同问题的描述,请参阅 this Fluxxor issue。

【讨论】:

以上是关于React Flux - 在调度中调度 - 如何避免?的主要内容,如果未能解决你的问题,请参考以下文章

React 和 Flux:“在调度中间调度”以显示来自 API 调用的错误消息

在 Flux/React 中调度级联/依赖异步请求

React Flux 实现调度链

React Native + Flux,无响应的调度程序

react/flux- 子组件用户事件 - 一切都应该通过调度程序路由吗

React、Flux、React-Router 调度错误 - 可能的批处理更新解决方案?