在组件间共享状态
有时,你会希望两个组件的状态始终保持同步。要实现这一点,需要从这两个组件中移除状态,将其提升到它们最近的共同父组件中,然后通过 props 向下传递。这被称为状态提升,也是编写 React 代码时最常做的事情之一。
您将学习
- 如何通过状态提升在组件间共享状态
- 什么是受控组件和非受控组件
通过示例学习状态提升
在这个例子中,父组件Accordion渲染了两个独立的Panel组件:
AccordionPanelPanel
每个Panel组件都有一个布尔类型的isActive状态,用于决定其内容是否可见。
点击两个面板的“显示”按钮:
请注意,点击一个面板的按钮不会影响另一个面板——它们是相互独立的。


最初,每个Panel的isActive状态都是false,因此它们都显示为折叠状态。


点击任意一个Panel的按钮只会更新该Panel自身的isActive状态。
但现在假设你想修改设计,使得在任何时候都只展开一个面板。按照这个设计,展开第二个面板时应该折叠第一个面板。你会如何实现?
要协调这两个面板,你需要通过三个步骤将它们的状态“提升”到父组件:
- 移除子组件中的状态。
- 从共同父组件传递硬编码的数据。
- 向共同父组件添加状态,并将其与事件处理函数一起向下传递。
这将允许Accordion组件协调两个Panel组件,并确保每次只展开一个。
步骤 1:从子组件中移除状态
你将把Panel的isActive状态的控制权交给其父组件。这意味着父组件会将isActive作为 prop 传递给Panel。首先,从Panel组件中移除这一行:
相反,将 isActive添加到Panel的属性列表中:
现在,Panel的父组件可以通过向下传递属性isActive 来 控制它。 相反,Panel 组件现在对 没有控制权 的值 isActive——这完全取决于父组件!
步骤 2:从公共父组件传递硬编码数据
要提升状态,你必须找到你想要协调的 两个 子组件的最接近的公共父组件:
Accordion(最接近的公共父组件)PanelPanel
在这个例子中,它是Accordion组件。由于它位于两个面板之上,并且可以控制它们的属性,因此它将成为哪个面板当前处于活动状态的“单一事实来源”。让Accordion 组件将一个硬编码的 isActive值(例如,true)传递给两个面板:
尝试编辑 Accordion组件中的硬编码isActive 值,并查看屏幕上的结果。
步骤 3:向公共父组件添加状态
提升状态通常会改变你存储为状态的内容的性质。
在这种情况下,一次应该只有一个面板处于活动状态。这意味着公共父组件Accordion 需要跟踪 哪个面板是活动面板。它可以使用一个数字作为活动布尔 的索引,而不是 Panel值作为状态变量:
当 activeIndex 为 0时,第一个面板处于活动状态;当它为1时,第二个面板处于活动状态。
点击任一 Panel中的“显示”按钮都需要更改Accordion中的活动索引。一个Panel不能直接设置activeIndex 状态,因为它是在 Accordion内部定义的。Accordion 组件需要 明确允许 Panel 组件通过 向下传递事件处理函数作为属性来更改其状态:
现在,<button> 内部的 Panel 将使用 onShow属性作为其点击事件处理函数:
至此,状态提升就完成了!将状态移动到共同的父组件中,使你能够协调两个面板。使用活动索引而不是两个“是否显示”标志,确保了在给定时间只有一个面板处于活动状态。将事件处理函数传递给子组件,则允许子组件改变父组件的状态。


最初,Accordion的activeIndex 是 0,因此第一个Panel接收到isActive = true


当 Accordion的activeIndex 状态变为 1时,第二个Panel将接收到isActive = true。
每个状态的单一数据源
在 React 应用中,许多组件将拥有自己的状态。有些状态可能“驻留”在靠近叶子组件(树底部的组件)的位置,例如输入框。其他状态可能“驻留”在应用顶部附近。例如,即使是客户端路由库,通常也是通过将当前路由存储在 React 状态中,并通过 props 向下传递来实现的!
对于每个独立的状态片段,你将选择“拥有”它的组件。 这一原则也被称为拥有 “单一数据源”。这并不意味着所有状态都存放在一个地方——而是对于每个状态片段,都有一个特定的 组件来保存该信息。与其在组件之间复制共享状态,不如将其 提升到它们共同的父组件中,然后向下传递给需要它的子组件。
你的应用会随着你的工作而改变。在弄清楚每个状态片段“驻留”在何处时,将状态向下移动或向上移动是很常见的。这都是过程的一部分!
要了解在包含更多组件的实践中这种感觉如何,请阅读React 哲学。
回顾
- 当你想协调两个组件时,将它们的状态提升到共同的父组件中。
- 然后通过共同的父组件向下传递信息。
- 向下传递事件处理函数,以便子组件可以改变父组件的状态。
- 将组件视为“受控”(由 props 驱动)或“非受控”(由 state 驱动)是很有用的。
