我对这些陈述感到有点困惑:"呈现整个应用程序"和"将状态传递给子组件"。
我有一个AppComponent
和TodosListComponent
的待办事项应用。 AppComponent
从商店中获取待办事项数组,并将其作为属性传递给TodosListComponent
。
我有很多状态的巨大应用程序。我有50个组件构建我的应用程序。我是否想要将商店中的所有州从AppComponent
向下传递到所有50个组件?
所以我想知道,会议是什么?让个别组件直接听他们关心的商店对我来说更有意义。优点是只有单个组件会重新渲染,但为什么然后整个应用程序的概念会在状态变化时重新出现"?
每种的优点和缺点是什么?什么是共同惯例?
答案 0 :(得分: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>
特别是,在顶层和最终子节点之间可能存在很多组件,除了传递它们之外,它们不对数据做任何事情,将这些组件更紧密地耦合到它们在层次结构中的位置。
总的来说,我喜欢这种技术提供的可调试性,但随着应用程序变得越来越大,越来越复杂,我发现仅仅使用一个顶级组件就不会这样做。
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
可能会变得有点棘手。
另一方面,您可以根据存储更改事件授予特定于应用程序(即不可重用)的子组件以访问存储并构建其自己的状态。像这样构建自己状态的组件有时被称为&#34;控制器视图&#34;或者,更常见的是,#34;容器组件。&#34;
当然,好处是,您根本不必处理传递属性(除了更改处理程序和更多可重用组件的属性之外)。
但缺点是,您的组件与存储的耦合程度更高 - 更改存储或它们提供的数据(或者它们提供数据的接口)可能会迫使您重新访问更大数量的代码组件。
此外,正如评论中所提到的,这可能会使服务器渲染变得更加困难。如果您只使用属性(特别是仅在顶层),则可以更轻松地将它们传输到客户端并使用相同的属性重新初始化React。通过允许商店确定自己的数据,您需要以某种方式将数据注入商店以允许组件获取该数据。
一种常见的方法,也就是我现在经常使用的方法,是让应用程序中的每个组件仅依赖于全局应用程序状态的props,然后决定是否更有意义(1)将它们直接连接到flux将它们包装在容器中,或(2)允许从某个父容器传递道具。
您可以使用抽象来使这些技术更加可行。例如,Facebook开发人员在a comment on Hacker News中有这样的说法:
现在您的所有数据都在商店中,但是如何将其纳入需要它的特定组件?我们从大型顶级组件开始,这些组件可以提取孩子们所需的所有数据,并通过道具将其传递下去。这导致中间组件中的许多残缺和无关的代码。我们在大多数情况下确定的是组件声明和获取他们自己需要的数据,除了一些小的,更通用的组件。由于我们的大多数数据都是异步获取和缓存的,因此我们创建了mixins,可以很容易地声明组件需要哪些数据,并将获取和监听更新挂钩到生命周期方法(componentWillMount等)。
答案 1 :(得分:4)
Jason Bonta来自Facebook explained概念&#34;容器&#34;在他的React.js Conf 2015演讲中。
总结一下:容器只是包装其他组件的组件,并处理任何与数据相关的问题,例如与商店交谈,而底层组件只关注视图(标记/样式/等),并且不会关心数据的来源。
这使组件
高度可重复使用,因为当数据需要来自不同的地方时,它可以用不同的容器包装,
不包含不相关的状态,因此更容易实施和优化shouldComponentUpdate
,
使用合成而非mixin与what is likely the future of React与ES6对齐,后者没有惯用的mixin。
更新 2019年3月:查看React Hooks。使用钩子,您可以实现与上述相同的目标,但是可以应用于多个组件的可重用代码块中与数据相关的抽象问题(例如与商店交谈)。 Dan Abramov的ReactConf讲话React Today and Tomorrow and 90% Cleaner React With Hooks在解释钩子方面做得非常出色,以及它们与mixins和过去的组合方法有何不同。