如何在 Redux 应用程序中动态/任意创建额外的 reducer 或避免获取竞争条件

Posted

技术标签:

【中文标题】如何在 Redux 应用程序中动态/任意创建额外的 reducer 或避免获取竞争条件【英文标题】:How to dynamically/arbitrarily create additional reducers in Redux app or avoid fetch race conditions 【发布时间】:2018-08-18 12:12:58 【问题描述】:


更新

此处提供的 Redux 示例可能对其他人具有指导意义: https://github.com/reactjs/redux/tree/master/examples/tree-view


更新

感谢您的 cmets。我仍在对此进行调查,但我目前正在探索类似于@Chase DeAnda 建议的方法。但是,我使用的对象不是数组,而是键等于父组件,值等于过去子组件的化简器的对象。这种方法似乎有效,但它仍然是 WIP。缺点是父组件的深度嵌套的 reducer。

这是 Redux 文档中讨论的模式: https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape

上面的链接还讨论了在其他更好的设计模式中解决嵌套问题的方法。我正在使用此文档来获得我想要的结果。

一旦我走得更远,我会用我的结果更新这个问题,也许我们会从那里继续。希望结果对处于类似情况的其他用户有用。再次感谢!


原始问题

对于以下使用 fetch 请求创建竞争条件的场景,我找不到设计建议:

    有一些父组件可以由用户动态创建。 每个父节点都有 5 个子节点,它们都发出 fetch 请求。这些孩子都有自己的减速器来促进这一点。 如果我在应用程序中创建一个新的父级,我要么需要创建新的子级减速器,要么取消先前活动父级的所有飞行请求并为当前活动的父级发起新请求。

有没有人遇到过类似的情况?我在这里阅读并尝试了 Dan 的代码拆分回复:

https://***.com/a/33044701/4240734

How to avoid race conditions when fetching data with Redux?

但是上面描述的场景看起来不同。一方面,我想根据非路由事件更改活动切片缩减器。在这种情况下,如果不违反设计原则,我就无法访问商店。此外,即使我确实可以访问商店,我也不知道 replaceReducer 是否提供了我希望它具有的行为。

我还在这里查看了 Dan Abramov 的 Egghead 教程:

https://egghead.io/lessons/javascript-redux-avoiding-race-conditions-with-thunks

事实上,我已经实施了他的解决方案来避免子组件之间的竞争条件。在 组件之间切换时会出现额外的复杂性。

我愿意接受任何建议。也可能是我的设计模式已经关闭,所以不介意为架构提供更好的解决方案。

TLDR;

    对于页面上的给定路由,我有任意数量的父组件。 每个父组件都有特定数量的子组件,它们都需要自己的 reducer 来管理使用 Fetch 启动的 req_sent、req_succeeded、req_failed(不是 xhr 或其他具有很好支持的中止选项的选项)。 如果用户创建了更多父组件(例如,针对不同的主题),则需要发生以下两种情况之一:
      为新创建的父组件的新创建子组件创建更多子减速器并将其添加到存储中。或 之前活动的父级的所有正在进行的请求都需要中止(再次使用 Fetch 请求),并且新激活的父级组件允许新的子级请求发出并填充现有的子级减速器。

否则,我最终会遇到来自子减速器的竞争条件,其中填充了错误父组件的数据。

【问题讨论】:

为什么不是同一个reducer的多个副本,而是一个可以包含每个子实例数组的reducer? hmmm.. 我觉得这是正确的方向。但让我再想一想。 您的用例中是否有一些东西使 redux 成为比使用组件状态更好的选择?如果组件状态有效并且每个组件都可以处理存储数据以及与获取其数据相关的状态,那么可能会使生活变得更简单。 你确定你真的需要动态创建新的reducer吗?您提到的这些子组件和父组件必须具有共同点。这意味着它们的行为只能在每个 reducer 中建模。如果新请求是由用户交互动态产生的,那么听起来你不应该在请求生命周期中使用 redux 状态。正如@TLadd 指出的那样,我会使用组件状态,并且只存储真正需要在 redux 中持久保存的内容。 感谢您的 cmets。我仍在对此进行调查,但我目前正在探索一种类似于@Chase DeAnda 建议的方法。但是,我使用的对象不是数组,而是键等于父组件,值等于过去子组件的化简器的对象。这种方法似乎有效,但它仍然是 WIP。缺点是父组件的深度嵌套的 reducer。但我将探索减轻这种情况的方法。一旦我走得更远,我会用我的结果更新这个问题,也许我们会从那里继续。再次感谢! 【参考方案1】:

如果一次只有一个活动的父组件,您可以简单地将父组件的某种 ID 附加到每个单独的 fetch 操作。

因此,如果调度了一个过时的父组件的成功操作,例如:

 type: 'CHILD_FETCH_SOMETHING_SUCCESS', payload:  parentID: 'someParent123', data:  ...  

..你会检查 someParent123 在这个时间点是否仍然是一个活动的父组件。如果没有,数据可以被丢弃,状态不会改变。

【讨论】:

感谢您的建议。我实际上正在实施该解决方案的一部分。但是,发出这些请求然后转储所有请求(当前活动的父级除外)的成本很高。相反,我将它们作为 key=parent/value=child 对存储在状态树中。这样,用户可以启动多个不同的父组件,并将结果保存在成功时的状态中。

以上是关于如何在 Redux 应用程序中动态/任意创建额外的 reducer 或避免获取竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

react + redux + saga +服务器端渲染+如何停止服务器端渲染页面的额外ajax调用?

带有额外参数的 redux thunk 是如何工作的?

动态生成 Redux Reducer

如何使用 redux-toolkit 动态更改基本 URL?

服务器端 React-Redux,Express,没有额外的 ES6 和 JSX 语法

如何利用java的反射机制动态创建对象