为啥我们在 Flux/Redux 架构中解耦 action 和 reducer?

Posted

技术标签:

【中文标题】为啥我们在 Flux/Redux 架构中解耦 action 和 reducer?【英文标题】:Why we decouple actions and reducers in Flux/Redux architecture?为什么我们在 Flux/Redux 架构中解耦 action 和 reducer? 【发布时间】:2017-09-24 05:26:14 【问题描述】:

我很长一段时间都在先使用 Flux,然后再使用 Redux,我确实喜欢它们,我看到了它们的好处,但我脑海中不断浮现一个问题: p>

为什么我们要解耦动作和reducer,并在表达改变状态(动作)的意图的调用和改变状态的实际方式(reducer)之间添加额外的间接,这样更难提供静态或运行时保证和错误检查?为什么不直接使用修改状态的方法或函数?

方法或函数将提供静态保证(使用 Typescript 或 Flow)和运行时保证(未找到方法/函数等),而未处理的操作将不会引发任何错误(静态或运行时),您将只需要看看预期的行为没有发生。

让我用我们的理论状态容器 (TSC) 更好地举例说明:

超级简单 将其视为 React 组件的状态接口(setState、this.state),没有渲染部分。

因此,您唯一需要做的就是在我们的 TSC 中的状态发生变化时触发组件的重新渲染以及更改该状态的可能性,在我们的例子中,这将是修改该状态的简单方法:@987654322 @、setErrorsetLoading

我所看到的是,actions 和 reducer 是动态或静态代码调度的解耦,所以不是调用myStateContainer.doSomethingAndUpdateState(...),而是调用actions.doSomethingAndUpdateState(...),你让整个flux/redux 机器连接那个action到状态的实际修改。整个事情还带来了 thunk、sagas 和其他中间件来处理更复杂的操作的必要性,而不仅仅是使用常规的 javascript 控制流。

主要问题是这种解耦需要您编写很多东西来实现这种解耦: - 动作创建函数的接口(参数) - 动作类型 - 动作有效载荷 - 你的状态的形状 - 你如何更新你的状态

将此与我们的理论状态容器 (TSC) 进行比较: - 你的方法的接口 - 你的状态的形状 - 你如何更新你的状态

那么我在这里错过了什么?这种脱钩有什么好处?

这与另一个问题非常相似:Redux actions/reducers vs. directly setting state

让我解释一下为什么这个问题的投票最多的答案既没有回答我的问题,也没有回答原来的问题: - Actions/Reducers 让你问谁和如何?这可以通过我们的 TSC 来完成,它只是一个实现细节,与 action/reducers 本身无关。 - Actions/Reducers 让你回到你的状态:这又是一个状态容器的实现细节问题,可以通过我们的 TSC 来实现。 - 等等:状态变更指令、中间件,以及目前通过 action/reducers 实现的任何东西都可以通过我们的 TSC 来实现,这只是实现它的问题。

非常感谢! 弗兰

【问题讨论】:

【参考方案1】:

其中一个主要原因是通过操作限制状态更改允许您将所有状态更改视为仅取决于操作和先前状态,这简化了对每个操作中正在发生的事情的思考。该架构将与“现实世界”的任何类型的交互“捕获”到动作创建者功能中。因此,可以将状态更改视为事务。

在您的理论状态容器中,状态更改可能随时发生不可预测的情况并激活各种副作用,这会使它们更难推理,也更难发现错误。 Flux 架构强制将状态更改视为离散事务流。

另一个原因是将代码中的数据流限制在一个方向上。如果我们允许任意不受约束的状态修改,我们可能会得到状态更改,导致更多状态更改导致更多状态更改......这就是为什么在 reducer 中调度操作是一种反模式。我们想知道每个动作的来源,而不是创建级联动作。

Flux 的创建是为了解决 Facebook 的一个问题:当某些界面代码被触发时,可能会导致一连串几乎不可预测的副作用,每个副作用相互导致。 Flux 架构通过使每个状态转换都成为单向的事务和数据流,使这成为不可能。

但是,如果执行此操作所需的样板文件让您感到困扰,您可能会很高兴知道您的“理论状态容器”或多或少存在,尽管它比您的示例复杂一些。它叫做MobX。

顺便说一句,我认为您对整个“这是一个实现细节”的事情有点过于乐观了。我认为,如果您尝试为您的 Theoretical State Container 实际实现时间旅行调试,那么您最终得到的结果实际上与 Redux 非常相似。

【讨论】:

您也可以将状态更改视为 TSC 中的事务,想想 React 的 setState(oldState => newState),也可以事务处理。我的假设是跟踪setState 的调用与跟踪正在调度的操作相同(实际上,this.setState 实际上是调度与上下文this 关联的函数,因此相似之处更深)。然后,数据流仍然是一个方向:方法是唯一可以改变状态的东西,状态改变会触发重新渲染,这是一种单向流。 重要的是我的假设和可能我的问题是,如果可以为您提供一种数据流、静态类型检查、事务状态更改的方式,而无需添加操作和减速器所需的间接,有什么不好的地方? 它的可扩展性较差,导致架构更加僵化,因为动作往往与其效果紧密结合,从而使重构更加困难。将操作与处理程序分离可以使架构更加灵活,这在开发具有不断变化的需求的大型应用程序(Flow 的原始用例)时非常重要。例如,当使用 Flow 时,使动作对以前不存在的状态部分产生影响是微不足道的。但除此之外,没有问题,正如 MobX 的存在所证明的那样,它几乎完全做到了这一点。 Redux(以及一般的 Flux)最初是用于样板文件不是问题的大型应用程序。 (顺便说一句,我稍后会将所有这些添加到我的答案中) 这开始变得更有意义了。我仍然假设 TSC 可能没有那么僵化,静态类型检查器的情况会更好,并且通过无类型、未经检查的调度将动作和减速器解耦可能不是那么通用。或者差异不是那么大。从某种意义上说,它让我想起了 Backbone Marionette 框架中的事件管道,它减少了模块之间的耦合,但使故障更难以检测。我一定会检查更多关于 MobX 的信息。非常感谢!

以上是关于为啥我们在 Flux/Redux 架构中解耦 action 和 reducer?的主要内容,如果未能解决你的问题,请参考以下文章

flux,redux,vuex状态集管理工具之间的区别

API 令牌在 Flux (Redux) 存储中是不是安全?

在 Flux / Redux 中保存啥和不保存啥?

Flux/Redux 在动作结束时触发副作用

低价彻底征服React.js+Flux+Redux

使用 Firebase 实时数据库,我还需要使用flux、redux、mobx 或relay 和graphql