REACT 在组件之间共享状态

简介: REACT 在组件之间共享状态

有时,您希望两个组件的状态始终一起变化。要做到这一点,请从他们俩身上删除状态,将其移动到他们最近的共同父级,然后通过道具将其传递给他们。这被称为提升状态,这是编写 React 代码时最常见的事情之一。

举例提升状态

在此示例中,父组件呈现两个单独的 s:AccordionPanel

  • Accordion
  • Panel
  • Panel

每个组件都有一个布尔状态,用于确定其内容是否可见。PanelisActive

按两个面板的显示按钮:

import { useState } from 'react';
 
function Panel({ title, children }) {
  const [isActive, setIsActive] = useState(false);
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Show
        </button>
      )}
    </section>
  );
}
 
export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About">
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology">
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

请注意,按下一个面板的按钮不会影响另一个面板,因为它们是独立的。


最初,每个 的状态都是 ,所以它们看起来都折叠了PanelisActivefalse


单击任一按钮只会单独更新该状态PanelPanelisActive

但现在假设您要更改它,以便在任何给定时间仅展开一个面板。在这种设计下,展开第二个面板应该会折叠第一个面板。你会怎么做?

要协调这两个面板,您需要通过三个步骤将其状态“提升”到父组件:

  1. 从子组件中删除状态。
  2. 从公共父级传递硬编码数据。
  3. 将状态添加到公共父级,并将其与事件处理程序一起传递。

这将允许组件协调两个 s,并且一次只能展开一个。AccordionPanel

步骤 1:从子组件中删除状态

您将把 的控制权交给其父组件。这意味着父组件将作为 prop 传递。首先从组件中删除以下行PanelisActiveisActivePanelPanel

const [isActive, setIsActive] = useState(false);


取而代之的是,添加到道具列表中:isActivePanel

function Panel({ title, children, isActive }) {


现在,父组件可以通过将其作为 prop 传递来控制。相反,组件现在无法控制 - 现在由父组件决定!PanelisActivePanelisActive

步骤 2:从公共父级传递硬编码数据

若要提升状态,必须找到要协调的两个子组件中最接近的公共父组件:

  • Accordion(最接近的共同父级)
  • Panel
  • Panel

在此示例中,它是组件。由于它位于两个面板的上方,并且可以控制它们的道具,因此它将成为面板当前处于活动状态的“真相来源”。使组件将硬编码值(例如,)传递给两个面板:AccordionAccordionisActivetrue

import { useState } from 'react';
 
export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About" isActive={true}>
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology" isActive={true}>
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}
 
function Panel({ title, children, isActive }) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Show
        </button>
      )}
    </section>
  );
}

 

尝试编辑组件中的硬编码值,并在屏幕上查看结果。isActiveAccordion

步骤 3:将状态添加到公共父级

提升状态通常会改变存储为状态的内容的性质。

在这种情况下,一次只能有一个面板处于活动状态。这意味着公共父组件需要跟踪哪个面板是活动面板。它可以不使用值,而是使用数字作为状态变量的活动索引:AccordionbooleanPanel

const [activeIndex, setActiveIndex] = useState(0);


当 是 时,第一个面板处于活动状态,当它处于活动状态时,它是第二个面板。activeIndex01

单击其中的“显示”按钮需要更改 中的活动索引。A 不能直接设置状态,因为它是在 .组件需要显式允许组件通过将事件处理程序作为 prop 向下传递来更改其状态:PanelAccordionPanelactiveIndexAccordionAccordionPanel

<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>


内部现在将使用道具作为其 click 事件处理程序:<button>PanelonShow

import { useState } from 'react';
 
export default function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel
        title="About"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel
        title="Etymology"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}
 
function Panel({
  title,
  children,
  isActive,
  onShow
}) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}>
          Show
        </button>
      )}
    </section>
  );
}

这样就完成了提升状态!将状态移动到公共父组件中允许您协调两个面板。使用活动索引而不是两个“显示”标志可确保在给定时间只有一个面板处于活动状态。将事件处理程序传递给子级允许子级更改父级的状态。

每个州的单一事实来源

在 React 应用程序中,许多组件将有自己的状态。某些状态可能像输入一样“存在于”叶子组件(树底部的组件)附近。其他状态可能“生活”在更靠近应用程序顶部的位置。例如,即使是客户端路由库,通常也是通过将当前路由存储在 React 状态,并通过 props 传递来实现的!

对于每个唯一的状态,您将选择“拥有”它的组件。这一原则也被称为具有“单一事实来源”。这并不意味着所有状态都存在于一个地方,而是对于每个状态,都有一个特定的组件来保存该信息。与其在组件之间复制共享状态,不如将其提升到其共同的共享父级,并将其传递给需要它的子级。

你的应用会随着你的使用而改变。通常,当您仍在弄清楚状态的每个部分“居住”的位置时,您会向下或向上移动状态。这都是过程的一部分!

相关文章
|
8天前
|
前端开发 JavaScript 开发者
React 按钮组件 Button
本文介绍了 React 中按钮组件的基础概念,包括基本的 `&lt;button&gt;` 元素和自定义组件。详细探讨了事件处理、参数传递、状态管理、样式设置和可访问性优化等常见问题及其解决方案,并提供了代码示例。帮助开发者避免易错点,提升按钮组件的使用体验。
117 77
|
5天前
|
存储 前端开发 UED
React 面包屑组件 Breadcrumb 详解
面包屑导航是现代Web应用中常见的UI元素,帮助用户了解当前位置并快速返回上级页面。本文介绍如何使用React构建面包屑组件,涵盖基本概念、实现方法及常见问题。通过函数式组件和钩子,结合React Router动态生成路径,处理嵌套路由,并确保可访问性。示例代码展示了静态和动态面包屑的实现,帮助开发者提升用户体验。
102 73
|
9天前
|
前端开发 UED 开发者
React 对话框组件 Dialog
本文详细介绍了如何在 React 中实现一个功能完备的对话框组件(Dialog),包括基本用法、常见问题及其解决方案,并通过代码案例进行说明。从安装依赖到创建组件、添加样式,再到解决关闭按钮失效、背景点击无效、键盘导航等问题,最后还介绍了如何添加动画效果和处理异步关闭操作。希望本文能帮助你在实际开发中更高效地使用 React 对话框组件。
115 75
|
1月前
|
前端开发 JavaScript 测试技术
React 分页组件 Pagination
本文介绍了如何在 React 中从零构建分页组件,涵盖基础概念、常见问题及解决方案。通过示例代码详细讲解了分页按钮的创建、分页按钮过多、初始加载慢、状态管理混乱等常见问题的解决方法,以及如何避免边界条件、性能优化和用户反馈等方面的易错点。旨在帮助开发者更好地理解和掌握 React 分页组件的开发技巧,提升应用的性能和用户体验。
76 0
|
1月前
|
设计模式 前端开发 编译器
与普通组件相比,React 泛型组件有哪些优势?
与普通组件相比,React 泛型组件有哪些优势?
103 63
|
14天前
|
前端开发 Java API
React 进度条组件 ProgressBar 详解
本文介绍了如何在 React 中创建进度条组件,从基础实现到常见问题及解决方案,包括动态更新、状态管理、性能优化、高级动画效果和响应式设计等方面,帮助开发者构建高效且用户体验良好的进度条。
42 18
|
28天前
|
存储 前端开发 测试技术
React组件的最佳实践
React组件的最佳实践
|
27天前
|
前端开发 API 开发者
React 文件上传组件 File Upload
本文详细介绍了如何在 React 中实现文件上传组件,从基础的文件选择和上传到服务器,再到解决文件大小、类型限制、并发上传等问题,以及实现多文件上传、断点续传和文件预览等高级功能,帮助开发者高效构建可靠的应用。
54 12
|
22天前
|
存储 前端开发 JavaScript
React 表单输入组件 Input:常见问题、易错点及解决方案
本文介绍了在 React 中使用表单输入组件 `Input` 的基础概念,包括受控组件与非受控组件的区别及其优势。通过具体代码案例,详细探讨了创建受控组件、处理多个输入字段、输入验证和格式化的方法,并指出了常见易错点及避免方法,旨在提升表单的健壮性和用户体验。
33 4
|
29天前
|
前端开发 JavaScript API
React 文件下载组件 File Download
本文介绍了在React中实现文件下载组件的方法,包括使用`a`标签和JavaScript动态生成文件,解决了文件路径、文件类型、大文件下载及文件名乱码等问题,并展示了使用第三方库`file-saver`和生成CSV文件的高级用法。
38 6