Redux-多家商店,为什么不呢?


220

注意:我已经阅读了Redux(Baobab,也是如此)的文档,并且做了很多Google搜索和测试工作。

为什么强烈建议Redux应用程序只有一个商店?

我了解单商店设置与多商店设置的优缺点(关于此主题,有很多关于SO的问答)。

IMO,这个架构决定是基于应用程序开发人员的项目需求。那么,为什么如此强烈地建议Redux使用,几乎达到强制性的程度(尽管没有什么阻止我们开设多个商店)?

编辑:转换为单店后的反馈

在与redux一起工作了几个月后,许多人认为它是复杂的SPA,我可以说单一的商店结构让我感到很高兴。

以下几点可能会帮助其他人理解为什么在许多很多用例中,单商店还是多商店是一个有争议的问题:

  • 可靠:我们使用选择器来挖掘应用程序状态并获取与上下文相关的信息。我们知道所有需要的数据都在一个存储中。它避免了所有关于国家问题可能在哪里的质疑。
  • 速度很快:我们的商店目前有将近100个减速器,如果没有更多的话。即使达到这个数量,也只有极少数的减速器在任何给定的调度上处理数据,其他的只是还原以前的状态。关于大型/复杂存储(reducers的nbr)缓慢的说法几乎没有意义。至少我们还没有看到任何性能问题。
  • 调试友好:虽然这是整体上使用redux的最有说服力的参数,但对于单商店还是多商店也是如此。在构建应用程序时,您一定会在过程中出现状态错误(程序员错误),这很正常。PITA是那些错误需要数小时才能调试的时候。多亏了单一存储(和redux-logger),我们从未在任何给定的状态问题上花费超过几分钟的时间。

一些指针

建立您的redux商店的真正挑战是决定如何构建它。首先,因为改变结构只是一个主要的痛苦。其次,因为它在很大程度上决定了您的使用方式,并为任何过程查询应用数据。关于如何构建商店有很多建议。在我们的案例中,我们发现以下是理想的:

{
  apis: {     // data from various services
    api1: {},
    api2: {},
    ...
  }, 
  components: {} // UI state data for each widget, component, you name it 
  session: {} // session-specific information
}

希望此反馈对其他人有帮助。

编辑2-有用的商店工具

对于那些一直想知道如何“轻松”管理一家商店的人来说,这可能会很快变得复杂。有一些工具可以帮助隔离商店的结构依赖性/逻辑。

Normalizr可以根据架构对数据进行标准化。然后,它提供了一个界面来处理您的数据,并通过来获取数据的其他部分id,就像一个Dictionary。

当时我还不了解Normalizr,所以我沿相同的思路建立了一些东西。Relational-json采用一个模式,并返回一个基于Table的接口(有点像一个数据库)。Relational-json的优点是您的数据结构可以动态引用数据的其他部分(本质上,您可以像正常的JS对象一样在任何方向上遍历数据)。它不如Normalizr成熟,但是我已经在生产中成功使用了几个月。


4
我喜欢您使用的商店结构方法;但是,如何处理api状态更改映射到组件状态更改?如此说来,我从我的API接收特定于域的数据,这如何转换为将在我的组件中找到的通用数据结构?
Diniden '16

实际上,组件如何映射/使用商店数据取决于您。尽管我认为我不完全理解您的问题,但是您可以详细说明或开始聊天吗?
塞巴斯蒂安·丹尼尔

2
我想的问题是:您的组件是从apis状态渲染任何东西,还是仅渲染被放入components状态的东西。我怀疑如果您仅从组件的状态进行渲染,那么即使在存在特定于域的数据的情况下,您也找到了一种极好的方法来使组件和容器具有高度可重用性。如果您的组件部分是从API状态和组件状态呈现的,那么我猜您正在利用特定于域的容器将api中的数据映射到组件可以理解的通用列表和原语。
Diniden '16

2
我将Redux与选择器结合使用,这些选择器基本上是功能齐全的纯数据映射器。每个组件都“反应”以存储更新,如果与之相关的更改,则它“选择”数据并进行相应渲染。因此,是的,组件仅根据对它们重要的对象进行渲染。但这不仅仅是因为Redux或商店结构。这是由于结合了不可变数据存储,数据更改的参考比较测试以及以组件所需格式获取组件所需数据的纯选择器。
塞巴斯蒂安·丹尼尔

嘿@SebastienDaniel,您能否显示一个示例,说明如何实施检查以使每个组件确实知道商店更新中的更改是否与此相关?我的意思是,如果您使用某种通用模式...或者在每种特定情况下,您都在检查与特定组件相关的数据是否已更改。
John Bernardsson

Answers:


232

在某些情况下,您可能会使用多个商店(例如,如果您在每秒多次刷新屏幕上的数千个项目的列表时遇到性能问题)。也就是说,这是一个例外,在大多数应用程序中,您只需要一个商店即可。

为什么我们在文档中强调这一点?因为大多数来自Flux背景的人都会认为多个商店是使更新代码模块化的解决方案。但是Redux对此有另一种解决方案:减速器组成。

在Redux中,如何将多个化简器进一步拆分成化简器树,是保持更新模块化的方式。如果您不了解这一点,并且在没有首先完全了解减速器组成的情况下前往多家商店,那么您将错过Redux单商店架构的许多好处:

  • 通过使用化简器组成,可以waitFor通过编写化简器以额外的信息并按特定顺序手动调用其他化简器来轻松实现Flux中的“相关更新” 。

  • 只需存储一个商店,就很容易持久,补充水分并读取状态。服务器渲染和数据预取很简单,因为在客户端上只需要填充和重新水化一个数据存储,JSON就可以描述其内容而无需担心存储的ID或名称。

  • 一家商店使Redux DevTools的时间旅行功能成为可能。它还使社区扩展(例如redux-undo或redux-optimist)变得容易,因为它们在减速器级别上运行。这样的“还原剂增强剂”不能用于商店。

  • 单个存储保证仅在处理分派之后才调用订阅。也就是说,在通知侦听器时,状态已完全更新。在许多商店中,没有这样的保证。这是Flux需要waitFor拐杖的原因之一。对于单个商店,这不是您首先遇到的问题。

  • 最重要的是,在Redux中不需要多个存储(除了性能边缘的情况外,无论如何您都应该首先对其进行概要分析)。我们在文档中将其作为重要的一点,因此鼓励您学习reducer的组成和其他Redux模式,而不要像使用Flux那样使用Redux并失去其优势。


11
我承认我不了解减速器组成的全部优点/必要性。感谢您的回答,我做了更多的阅读和示例(再次是TodoMVC)。用这么小的例子,很难理解减速机成分的实际改进。但是,经过一番思考,在很大程度上,(现在)收益是显而易见的。再次感谢,很棒的答案!
塞巴斯蒂安·丹尼尔

4
@Sebastien我认为“购物车”示例对此更好。
Dan Abramov 2015年

3
我正在将redux缓慢地实现到传统(非SPA)应用程序中。我正在为每个“整体”使用multilpe商店,然后将其转换为react / redux实现,直到可以将整个应用程序修改为使用同一商店。
Paul Knopf

5
@DanAbramov感到好奇的是,如果您的主“应用”运行自己的Redux商店,并通过npm导入一个运行自己的Redux商店的独立“应用”,情况会如何?例如,如果您公司中的其他团队中的某一个具有某种消息传递服务,并且您希望引入该UI而不用该数据污染您的商店。
natlee75 '16

6
@ natlee75“在Redux中使用多个存储的一些正当理由可能包括:[...]将Redux应用隔离为更大应用程序中的组件,在这种情况下,您可能希望针对每个根组件实例创建一个存储。” 来自redux.js.org/docs/FAQ.html#store-setup-multiple-stores
Kevin

24

在一些具有成百上千个减速器的大型企业应用程序中,将应用程序的不同区域视为完全独立的应用程序通常很有用。在那种情况下(实际上是多个应用程序共享一个域名),我会使用多个商店。

例如,我倾向于将以下常见功能区域视为单独的应用程序:

  • 管理员
  • 分析/数据与仪表板
  • 帐单管理和购买流程
  • 企业客户团队/权限管理

如果这些东西很小,请将其保留在主应用程序中。如果它们变得非常大(如某些企业帐户管理和分析工具那样),则将它们分开。

管理大型应用程序的最佳方法是将它们视为许多小型应用程序的组合。

如果您的应用程序的LOC少于50k LOC,则您可能应该忽略此建议,而应遵循Dan的建议。

如果您的应用程序的LOC超过100万,您可能应该拆分小型应用程序,即使您将其保存在单存储库中也是如此。


5

此架构决定应基于应用程序开发人员的项目需求

您生活在自己的世界中。我每天都在与使用redux的人见面,因为它很流行。您甚至无法想象没有任何决定就可以启动多少个项目。我讨厌redux方法,但不得不使用它,因为其他开发人员一无所知。这只是Facebook膨胀的史诗般的泡沫。

  • 不可靠因为商店的各个部分不是孤立的。
  • 效率低下因为您正在克隆和遍历哈希树。当突变以算术方式增长时,复杂性就会以几何方式增长。您无法通过重构任何化简器,选择器等来修复它。您必须拆分Trie。
  • 当它变慢时,没有人希望将其拆分为带有单独存储的单独应用程序。没有人愿意花钱在重构上。人们通常将一些智能组件转换为转储,仅此而已。您知道Redux开发人员正在等待什么未来吗?他们将维持这些地狱。
  • 它不是调试友好的。很难调试商店中几乎孤立的部分之间的连接。甚至很难分析这些连接的数量。

假设您有几个redux存储。您将中断单向数据流。您将立即意识到商店之间有多少联系。您可能会遭受这些连接的困扰,与圆形部门战斗等。

具有单向流动的单一不可变存储并不是每种疾病的灵丹妙药。如果您不想维护项目体系结构,无论如何都会遭受痛苦。


我喜欢你说的话,在这里我问了有关的问题。您是否愿意花些时间来看看并分享您的观点?我在Reddit中提出的问题,因为SO在这里不鼓励此类问题。
奥雅纳(Arup Rakshit)

3

多个存储在以下用例中可能会有所帮助1.如果您具有在数据结构,行为,应用程序上下文方面彼此独立的大型组件。隔离这些组件可以更轻松地管理数据和应用程序流。它还有助于独立开发和维护组件。2.性能问题:这不是典型的用例,但是如果您的某些组件更新非常频繁并且对其他组件没有任何影响,则可能可以去其他商店。

对于所有其他情况,您可能不需要拥有多个商店。正如Dan所说,创造周到的还原剂成分可以证明是更好的解决方案。


您的消息看起来像“除少数情况外,始终使用redux” ==“除少数情况外,您不需要项目体系结构”。非常接近现实,我很高兴。
puchu

2

为什么我们不能使用Redux使用多个存储????

在Redux中这不是必需的,因为数据域之间的分离已经通过将单个缩减器拆分为较小的缩减器来实现。


我可以还是应该创建多个商店?我可以直接导入我的商店,然后自己在组件中使用它吗?

原始的Flux模式描述了在一个应用程序中有多个“存储”,每个“存储”都保存着不同的域数据区域。这可能会带来一些问题,例如需要一个商店“ waitFor”另一商店进行更新。

在Redux中这不是必需的,因为数据域之间的分离已经通过将单个缩减器拆分为较小的缩减器来实现。

与其他几个问题一样,可以在一个页面中创建多个不同的Redux存储,但是预期的模式是只有一个存储。拥有一个存储可以启用Redux DevTools,使数据的持久化和重新水化更加简单,并简化了订阅逻辑。

在Redux中使用多个商店的一些有效原因可能包括:

通过对应用程序进行性能分析确认后,解决由于状态的某些部分过于频繁更新而导致的性能问题。将Redux应用程序隔离为更大应用程序中的组件,在这种情况下,您可能希望针对每个根组件实例创建一个商店。但是,创建新商店并不是您的第一本能,特别是如果您来自Flux背景。请先尝试使用reducer composition,如果无法解决您的问题,请仅使用多个商店。

同样,虽然您可以通过直接导入商店实例来引用商店实例,但在Redux中不建议使用这种模式。如果创建商店实例并从模块中导出它,它将成为单例。这意味着将Redux应用程序隔离为大型应用程序的组成部分(如果有必要)或启用服务器渲染将变得更加困难,因为要在服务器上为每个请求创建单独的存储实例。

redux的官方文档


1

在很多情况下,确实需要在Redux中拥有一家商店,我都使用Redux和Flux,并相信Redux可以做得更好!

不要忘记商店位于JavaScript对象中,因此,尽管只有一个商店,但是可以轻松扩展和重用它,对我来说,拥有一个商店可以使使用Redux开发工具进行遍历变得容易得多,而且不会在其中混用大型应用...

同样,一个商店的概念也为我们模仿了数据库,这是一个真理源,您可以更改它,也可以在浏览器内存中访问它。

如果整个应用程序得到良好的管理,那么一个商店就足以管理整个应用程序的状态...


3
因此,每个人都应该相信单一商店会做得更好,没有人能解释原因。它使我想起了什么……
puchu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.