反应通量 - 调度中的调度 - 如何避免



我似乎遇到了一种情况,即我无法避免 Flux 中的调度内调度问题。

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

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

场景

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

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

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

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

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

  1. 首先,登录按钮呈现在组件Wrapper
  2. 单击该按钮将调度LOGIN操作。
  3. 延迟后,将调度LOGIN_RESPONSE操作(通过 alt.js 中的异步机制)。
  4. 此操作会触发MyStore更新用户名。
  5. Wrapper组件观察存储更改并更新其状态。
  6. 这会导致Wrapper呈现Content组件而不是登录按钮。
  7. Content组件在装载时尝试调度FETCH_DATA操作,但由于调度程序仍在调度LOGIN_RESPONSE 而失败。(如果我将FETCH_DATA调度包装在setTimeout它可以工作,但感觉就像一个黑客)。

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

  • React - 身份验证过程:无法在调度过程中
  • 调度
  • 在 Flux/React 中调度级联/依赖异步请求
  • Flux Dispatch.dispatch(...):无法在调度过程中调度

此数据流是否存在固有问题?做这样的事情的正确 Flux 方法是什么?

这是

许多库中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/,有关不同框架中相同问题的描述,请参阅此 Fluxxor 问题。

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

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

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

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

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

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

由于异步调用,事情可能会变得有点混乱。可能会发生类似以下事件链的情况:

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

也许你的应用程序可以很好地处理这样的事件链,但这种"不可预测性"伤害了Flux单向数据流背后的基本动机。

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

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

最新更新