Redux / ngrx/store 架构:为什么不能从“哑组件”中分派操作?

13

我正在构建一个Angular 2 ngrx/store应用程序,并试图了解最佳实践。

  • 我喜欢拥有一个只基于分派操作更改的不可变状态,以便应用程序状态非常清晰且易于调试。
  • 我喜欢从“智能”容器向下进行单向数据流,因为这允许我们使用异步管道来减少对状态的检查。

但是我不明白为什么我们想要将来自哑组件的事件“冒泡”到智能组件之前,再将分派一个操作到存储中。唯一的原因是拥有可重用组件吗?在我看来,大多数组件根本不会被重用,因为很少有情况下我想要包括CSS在内的所有内容都完全相同。我是否错过了其他任何好处?从可维护性/可读性的角度来看,不仅在发生交互的组件中看到分派操作更好吗?


1
使用ng > 2一段时间后,我意识到ngrx/effect和smart-container是你可以选择的两种设计方式。如果你使用ngrx/effects,那么你不会感觉太需要使用Smart-Dumb组件。 - Harshal Patil
5个回答

11

首先,免责声明,我不是该主题的专家。

  • 我认为控制“笨拙组件”的智能组件实际上被称为中介者模式。使用此模式可确保较少组件必须处理store,从而增强了松耦合性。
  • 易于维护:如果您需要重构并批量重命名操作,则在较少位置存在该操作时更容易进行此操作。
  • 有一个处理actions的中央位置可以快速概览架构。同时,可以更轻松地进行store访问逻辑的热交换。
  • 正如已经提到的那样:复用。您可以在具有或不具有ngrx架构的项目之间共享和重用“笨拙组件”。
  • 也可以通过连接不同的inputsoutputs在同一项目中进行复用。例如:下拉菜单组件可以具有很多需要不同输入和输出的用例。

7

其中一个主要原因是重复使用。

在MVC方面,将你的智能组件视为控制器,而将你的哑组件视为视图。

想象一个哑组件,它为你的某个实体(模型)渲染一个编辑表单。哑组件处理显示表单和验证消息,但你可以在添加实体屏幕、编辑实体屏幕以及应用程序中其他地方的弹出对话框上重复使用该组件。每个这样的用例都需要具有相同验证的相同表单,但您可能会对“提交”执行非常不同的操作。调用该哑组件的智能组件在每种情况下可能是不同的智能组件。通过触发事件并将值传递给智能组件,您可以仅编写一次“视图”而执行非常不同的操作。


好的回答,这是一个很好的例子,可以在不同的用例中重复使用相同的表单。 - Patrick Hillert

3

我完全同意你的看法,也有同样的疑问。

我希望组件能够使用调度程序(对于 ngrx/store 来说就是存储本身)来发射操作,而不是将这个逻辑移到容器(实际应用程序)中。

这样,组件与容器解耦,容器无需了解操作内容:只需监听状态变化并传递必要的数据。

另一方面,Introduction to ngrx/store 推广了更智能的容器设计,它了解底层组件的许多细节。

坦白地说,我还看不出明显的胜利者: 我只是认为从组件分派操作更简单、更干净,更接近 Elm 架构,这是 Redux 的灵感来源之一。


1
请参阅Redux的创始人之一Dan Abramov的这篇文章:https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 他指出,他现在已经修订了他的方法,并且很高兴将“智能”组件嵌套在“哑”组件中(尽管他现在为它们使用不同的名称),并将其智能性视为实现细节。 - Mark Whitfeld
1
谢谢,实际上我已经读过了。但同时,我完全转向了Elm和一些React Native,我感觉像是重生了:D - pietro909

1
我希望您能扩展给出的答案。
当说到不从哑组件分派操作有助于可重用性时,除了允许您一次又一次地使用相同的组件之外,它还帮助您集成第三方组件。这些可能是开源组件,甚至是您的同事开发的组件,他不知道您如何使用NgRx操纵数据。
这样,您可以尽可能地保持代码通用、模块化和实现独立。
只是为了澄清,这都是关于建议的,有些情况下可能更明智采取不同的行动,但通常最好遵循惯例。

0

我在ngrx/example-app中没有找到任何关于将“bubble up”事件传递给顶层组件的参考资料。同时,在Rob的演讲中,我也没有听到相关内容(可能是我错过了什么)。

我只是按照示例使用所有的ngrx,目前看起来很好。使用ngrx/store存储数据,使用ngrx/effects链接操作(我可以简化),并且使用“中间件”作为“actions”的形象,描述了您可以对存储部分之一执行的所有操作。

然后,当它似乎最方便时,我使用action(我只确保文件中使用的所有操作与当前类相关)。


1
例如,在book-detail.ts中,它会发出添加和删除事件。然后在其父组件(book-view.ts)中,它接收该事件并分派一个动作。为什么不直接在book-detail中分派呢?这种情况并不太糟糕,因为只有一层。但是,如果您在更复杂的应用程序中有多个级别的组件,我可以看到输出事件“冒泡”可能非常烦人。 - David
1
似乎所有的“哑”组件都像md-input或button或者你可以获得的最简单的元素一样。你不想将存储逻辑绑定到这些组件上,因为如果你决定更改有关操作、减速器、效果或其他存储逻辑的任何内容,你将需要更新每个哑组件。如果您将所有逻辑保留在“根”组件中(例如页面-直接由路由器显示的元素),则只需更新其中的几个(易于访问)。所以基本上是维护健康应用程序。 - Piotr Grużewski
我不同意你的观点 @PiotrGrużewski:我认为属于消息传递(然后是操作)的逻辑应尽可能隐藏,以便容器可以专注于实际应用程序的逻辑而不是基础设施。在容器中公开事件,然后将其转换为操作在我看来是多余的。另一方面,我认为让组件直接使用存储还不是一个完美的解决方案:我们可能需要一个适当的调度器? - pietro909
1
@David:关于组件附带的CSS,我认为它不会影响可重用性,因为通常可以覆盖它。 而不应该修改的是代码,这肯定会损害捆绑包。 - pietro909

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接