有几种方法可以处理这个问题。我认为它们都是有效的,并且有自己的取舍。
获取所有状态并将其部分传递给孩子
这是您特别询问的技术。使用此方法,您将拥有一些可用于顶级组件的函数或方法,将存储中的所有数据转换为“状态大袋”,然后您将有选择地将这些数据片段传递给子组件。如果这些组件有自己的孩子,他们会根据需要传递它。
这种方法的好处是它使事情通常易于调试。如果您必须更改从存储中检索状态的方式,您只需在顶级组件中更改它 - 只要它以相同的名称传递,其他组件将“正常工作。 " 如果某条数据是错误的,您应该只需要查看一个地方即可找出原因。
这种技术的缺点是我称之为“道具爆炸”——你最终可能会传递很多属性。我在一个中型通量应用程序中使用了这种方法,顶层应用程序组件的片段如下所示:
<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>
特别是,在顶层组件和一些最终的子组件之间可能有很多组件,它们除了传递数据之外什么都不做,将这些组件更紧密地耦合到它们在层次结构中的位置。
总的来说,我喜欢这种技术提供的可调试性,尽管随着应用程序变得越来越大和越来越复杂,我发现仅使用单个顶级组件来执行此操作并不是一个好主意。
获取所有状态并将其作为一个对象传递
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>
缺点是处理状态形状的变化变得有点困难;您不仅要更改顶级组件,还必须跟踪使用该数据的任何地方并更改组件访问属性的方式。此外,shouldComponentUpdate
实施起来可能会有些棘手。
允许组件获得自己的状态
另一方面,您可以授予特定于应用程序(即不可重用)的子组件来访问存储并根据存储更改事件建立自己的状态。像这样构建自己的状态的组件有时称为“控制器视图”,或者现在更常见的是“容器组件”。
当然,好处是您根本不必处理传递属性(除了更改处理程序和属性以获得更多可重用组件)。
但是,缺点是您的组件与商店的耦合度更高——更改商店或它们提供的数据(或它们提供数据的接口)可能会迫使您重新访问更多组件的代码。
此外,正如评论中提到的,这可能会使服务器渲染更加困难。如果您只使用属性(尤其是仅在顶层),您可以更轻松地将它们传输到客户端并使用相同的属性重新初始化 React。通过允许商店确定自己的数据,您需要以某种方式将该数据注入到商店中以允许组件获取该数据。
一种常见的方法,也是我现在通常使用的一种方法,是让应用程序中的每个组件仅依赖于全局应用程序状态的 props,然后决定是否更有意义 (1) 通过将它们包装在一个容器,或者 (2) 允许从某个父容器传递道具。
您可以使用一些抽象来使其中一些技术更可行。例如,一位 Facebook 开发人员在 Hacker News 的评论中这样说:
现在您的所有数据都在存储中,但是您如何将其放入需要它的特定组件中呢?我们从大型顶级组件开始,这些组件提取其子级所需的所有数据,并通过 props 向下传递。这会导致中间组件中出现大量繁琐且不相关的代码。在大多数情况下,我们决定的是组件自己声明和获取他们需要的数据,除了一些更小、更通用的组件。由于我们的大部分数据都是异步获取和缓存的,因此我们创建了 mixin,可以轻松声明组件需要哪些数据,并将获取和侦听更新挂钩到生命周期方法(componentWillMount 等)。