将商店状态作为道具或每个组件访问全球商店?

chr*_*oni 24 javascript flux reactjs

我对这些语句感到有点困惑:"呈现整个应用程序"和"将状态传递给子组件".

例1:

我有一个带有AppComponent和的todos应用程序TodosListComponent.该AppComponent抓斗从商店待办事项的阵列,并将其作为一个属性的TodosListComponent.

例2:

我有很多状态的巨大应用程序.我有50个组件构建我的应用程序.我想AppComponent通过所有50个组件将所有状态从商店中传递下来吗?

所以我想知道,会议是什么?让个别组件直接听他们关心的商店对我来说更有意义.优点是只有单个组件重新渲染,但为什么然后"整个应用程序在状态变化上重新渲染"的概念呢?

各自的优点和缺点是什么?常见的惯例是什么?

Mic*_*ley 34

有几种方法可以解决这个问题.我认为它们都是有效的并且有自己的权衡.

获取所有州并将其中的部分传递给儿童

这是您特别询问的技术.使用此方法,您将拥有一些可用于顶级组件的功能或方法,可将存储中的所有数据转换为"大包状态",然后您将选择性地将此数据的一部分传递给子组件.如果这些组件有自己的孩子,他们会在必要时传递它们.

这种方法的优点是它使事情通常易于调试.如果你必须改变从商店中检索一个状态的方式,你只需要在顶层组件中更改它 - 只要它以相同的名称传递下去,其他组件将"正常工作". " 如果某些数据是错误的,您只需要在一个地方查找以找出原因.

我称之为"道具爆炸"的这种技术的缺点 - 你最终可以通过很多属性.我在中型通量应用程序中使用此方法,顶级应用程序组件的片段如下所示:

<section id="col-left">
  <Filters loading={this.state.loading}
            events={this.state.events}
            playbackRate={this.state.videoPlayback.playbackRate}
            autoPlayAudio={this.state.audioPlayback.autoPlay}
            role={this.state.role} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo videoUuid={this.state.session.recording_uuid}
                lowQualityVideo={this.state.session.low_quality_video_exists}
                playbackRate={this.state.videoPlayback.playbackRate} />
  <section id="transcript">
    <Transcript loading={this.state.loading}
                events={this.state.events}
                currentEvents={this.state.currentEvents}
                selection={this.state.selection}
                users={this.state.session.enrolled_users}
                confirmedHcs={this.state.ui.confirmedHcs}

                currentTime={this.state.videoPlayback.position}
                playing={this.state.videoPlayback.playing} />
  </section>
</section>
Run Code Online (Sandbox Code Playgroud)

特别是,在顶层和最终子节点之间可能存在许多组件,除了传递它们之外,它们不对数据做任何事情,更紧密地将这些组件耦合到它们在层次结构中的位置.

总的来说,我喜欢这种技术提供的可调试性,但随着应用程序变得越来越大,越来越复杂,我发现仅仅使用一个顶级组件就无法实现这一点.

获取所有状态并将其作为一个对象传递

Facebook的一位开发人员提到了这种技术.在这里,你将得到一个大包状态,如上所述,但你将传递整个事物(或它的整个子部分)而不是单个属性.通过React.PropTypes.shape在子组件中使用,您可以确保通过正确的属性.

好处是你通过较少的物业; 上面的例子可能看起来更像这样:

<section id="col-left">
  <Filters state={this.state} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo session={this.state.session}
                playback={this.state.videoPlayback} />
  <section id="transcript">
    <Transcript state={this.state} />
  </section>
</section>
Run Code Online (Sandbox Code Playgroud)

不利的一面是,处理国家形态的变化变得有点困难; 而不仅仅是更改顶级组件,您必须在任何地方跟踪使用该数据,并更改组件访问该属性的方式.此外,shouldComponentUpdate实施起来可能有点棘手.

允许组件获得自己的状态

另一方面,您可以授予特定于应用程序(即不可重用)的子组件以访问存储并根据存储更改事件构建自己状态.像这样构建自己状态的组件有时被称为"控制器视图",或者更常见的是"容器组件".

当然,好处是你根本不需要处理传递属性(除了更改处理程序和更多可重用组件的属性).

但缺点是,您的组件与存储的耦合程度更高 - 更改存储或它们提供的数据(或者它们提供数据的接口)可能会迫使您重新访问大量组件的代码.

此外,正如评论中所提到的,这可能会使服务器渲染变得更加困难.如果您只使用属性(特别是仅在顶层),则可以更轻松地将它们传输到客户端并使用相同的属性重新初始化React.通过允许商店确定自己的数据,您需要以某种方式将数据注入商店以允许组件获取该数据.

一种常见的方法,也就是我现在经常使用的方法,是让应用程序中的每个组件仅依赖于全局应用程序状态的props,然后决定是否更有意义(1)通过将它们包装在一起直接连接到flux容器,或(2)允许从某个父容器传递道具.


您可以使用抽象来使这些技术更加可行.例如,Facebook开发人员在黑客新闻评论中说:

现在您的所有数据都在商店中,但是如何将其纳入需要它的特定组件中?我们从大型顶级组件开始,这些组件可以提取孩子们所需的所有数据,并通过道具将其传递下来.这导致中间组件中的许多残缺和无关的代码.我们在大多数情况下确定的是组件声明和获取他们自己需要的数据,除了一些小的,更通用的组件.由于我们的大多数数据都是异步获取和缓存的,因此我们创建了mixins,可以很容易地声明组件需要哪些数据,并将获取和监听更新挂钩到生命周期方法(componentWillMount等)中.

  • 谢谢布兰登的好评.这完全指出了我的困惑!我认为这里也值得一提的是服务器端渲染.如果从"顶部组件"传递状态,则从"一个地方"传递状态时,使用服务器端渲染要容易得多.这里的最后一个解决方案要求您可能覆盖存储或使它们在服务器上的行为不同. (2认同)