React 18 在组件间共享状态

参考文章

在组件间共享状态

有时候,希望两个组件的状态始终同步更改。要实现这一点,可以将相关 state 从这两个组件上移除,并把 state 放到它们的公共父级,再通过 props 将 state 传递给这两个组件。这被称为“状态提升”,这是编写 React 代码时常做的事。

状态提升

在这个例子中,父组件 Accordion 渲染了 2 个独立的 Panel 组件。

  • Accordion
    • Panel
    • Panel

每个 Panel 组件都有一个布尔值 isActive,用于控制其内容是否可见。

请点击 2 个面板中的显示按钮:

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)}>显示</button>)}</section>);
}export default function Accordion() {return (<><h2>哈萨克斯坦,阿拉木图</h2><Panel title="关于">阿拉木图人口约200万,是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。</Panel><Panel title="词源">这个名字来自于 <span lang="kk-KZ">алма</span>,哈萨克语中“苹果”的意思,经常被翻译成“苹果之乡”。事实上,阿拉木图的周边地区被认为是苹果的发源地,<i lang="la">Malus sieversii</i> 被认为是现今苹果的祖先。</Panel></>);
}

我们发现点击其中一个面板中的按钮并不会影响另外一个,他们是独立的。

在这里插入图片描述

假设现在想改变这种行为,以便在任何时候只展开一个面板。在这种设计下,展开第 2 个面板应会折叠第 1 个面板。该如何做到这一点呢?”

要协调好这两个面板,我们需要分 3 步将状态“提升”到他们的父组件中。

  1. 从子组件中 移除 state 。
  2. 从父组件 传递 硬编码数据。
  3. 为共同的父组件添加 state ,并将其与事件处理函数一起向下传递。

这样,Accordion 组件就可以控制 2 个 Panel 组件,保证同一时间只能展开一个。

第 1 步: 从子组件中移除状态

将把 Panel 组件对 isActive 的控制权交给他们的父组件。这意味着,父组件会将 isActive 作为 prop 传给子组件 Panel。先从 Panel 组件中 删除下面这一行

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

然后,把 isActive 加入 Panel 组件的 props 中:

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

现在 Panel 的父组件就可以通过 向下传递 prop 来 控制 isActive。但相反地,Panel 组件对 isActive 的值 没有控制权 —— 现在完全由父组件决定!

第 2 步: 从公共父组件传递硬编码数据

为了实现状态提升,必须定位到想协调的 两个 子组件最近的公共父组件:

  • Accordion(最近的公共父组件)
    • Panel
    • Panel

在这个例子中,公共父组件是 Accordion。因为它位于两个面板之上,可以控制它们的 props,所以它将成为当前激活面板的“控制之源”。通过 Accordion 组件将硬编码值 isActive(例如 true )传递给两个面板:

import { useState } from 'react';export default function Accordion() {return (<><h2>哈萨克斯坦,阿拉木图</h2><Panel title="关于" isActive={true}>阿拉木图人口约200万,是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。</Panel><Panel title="词源" isActive={true}>这个名字来自于 <span lang="kk-KZ">алма</span>,哈萨克语中“苹果”的意思,经常被翻译成“苹果之乡”。事实上,阿拉木图的周边地区被认为是苹果的发源地,<i lang="la">Malus sieversii</i> 被认为是现今苹果的祖先。</Panel></>);
}function Panel({ title, children, isActive }) {return (<section className="panel"><h3>{title}</h3>{isActive ? (<p>{children}</p>) : (<button onClick={() => setIsActive(true)}>显示</button>)}</section>);
}

可以尝试修改 Accordion 组件中 isActive 的值,并在屏幕上查看结果。

第 3 步: 为公共父组件添加状态

状态提升通常会改变原状态的数据存储类型。

在这个例子中,一次只能激活一个面板。这意味着 Accordion 这个父组件需要记录 哪个 面板是被激活的面板。可以用数字作为当前被激活 Panel 的索引,而不是 boolean 值:

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

activeIndex0 时,激活第一个面板,为 1 时,激活第二个面板。

在任意一个 Panel 中点击“显示”按钮都需要更改 Accordion 中的激活索引值。 Panel 中无法直接设置状态 activeIndex 的值,因为该状态是在 Accordion 组件内部定义的。 Accordion 组件需要 显式允许 Panel 组件通过 将事件处理程序作为 prop 向下传递 来更改其状态:

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

现在 Panel 组件中的 <button> 将使用 onShow 这个属性作为其点击事件的处理程序:

import { useState } from 'react';export default function Accordion() {const [activeIndex, setActiveIndex] = useState(0);return (<><h2>哈萨克斯坦,阿拉木图</h2><Paneltitle="关于"isActive={activeIndex === 0}onShow={() => setActiveIndex(0)}>阿拉木图人口约200万,是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。</Panel><Paneltitle="词源"isActive={activeIndex === 1}onShow={() => setActiveIndex(1)}>这个名字来自于 <span lang="kk-KZ">алма</span>,哈萨克语中“苹果”的意思,经常被翻译成“苹果之乡”。事实上,阿拉木图的周边地区被认为是苹果的发源地,<i lang="la">Malus sieversii</i> 被认为是现今苹果的祖先。</Panel></>);
}function Panel({title,children,isActive,onShow
}) {return (<section className="panel"><h3>{title}</h3>{isActive ? (<p>{children}</p>) : (<button onClick={onShow}>显示</button>)}</section>);
}

这样,就完成了对状态的提升!将状态移至公共父组件中可以让你更好的管理这两个面板。使用激活索引值代替之前的 是否显示 标识确保了一次只能激活一个面板。而通过向下传递事件处理函数可以让子组件修改父组件的状态。

在这里插入图片描述

受控组件和非受控组件

通常我们把包含“不受控制”状态的组件称为“非受控组件”。例如,最开始带有 isActive 状态变量的 Panel 组件就是不受控制的,因为其父组件无法控制面板的激活状态。

相反,当组件中的重要信息是由 props 而不是其自身状态驱动时,就可以认为该组件是“受控组件”。这就允许父组件完全指定其行为。最后带有 isActive 属性的 Panel 组件是由 Accordion 组件控制的。

非受控组件通常很简单,因为它们不需要太多配置。但是当你想把它们组合在一起使用时,就不那么灵活了。受控组件具有最大的灵活性,但它们需要父组件使用 props 对其进行配置。

在实践中,“受控”和“非受控”并不是严格的技术术语——通常每个组件都同时拥有内部状态和 props。然而,这对于组件该如何设计和提供什么样功能的讨论是有帮助的。

当编写一个组件时,应该考虑哪些信息应该受控制(通过 props),哪些信息不应该受控制(通过 state)。当然,可以随时改变主意并重构代码。

每个状态都对应唯一的数据源

React 应用中,很多组件都有自己的状态。一些状态可能“活跃”在叶子组件(树形结构最底层的组件)附近,例如输入框。另一些状态可能在应用程序顶部“活动”。例如,客户端路由库也是通过将当前路由存储在 React 状态中,利用 props 将状态层层传递下去来实现的!

对于每个独特的状态,都应该存在且只存在于一个指定的组件中作为 state。这一原则也被称为拥有 “可信单一数据源”。它并不意味着所有状态都存在一个地方——对每个状态来说,都需要一个特定的组件来保存这些状态信息。应该 将状态提升 到公共父级,或 将状态传递 到需要它的子级中,而不是在组件之间复制共享的状态。

你的应用会随着你的操作而变化。当你将状态上下移动时,你依然会想要确定每个状态在哪里“活跃”。这都是过程的一部分!

摘要

  • 当想要整合两个组件时,将它们的 state 移动到共同的父组件中。
  • 然后在父组件中通过 props 把信息传递下去。
  • 最后,向下传递事件处理程序,以便子组件可以改变父组件的 state 。
  • 考虑该将组件视为“受控”(由 prop 驱动)或是“不受控”(由 state 驱动)是十分有益的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Socket交互的基本流程?

TCP socket通信过程图 什么是网络编程&#xff0c;网络编程就是编写程序使两台连联网的计算机相互交换数据。怎么交换数据呢&#xff1f;操作系统提供了“套接字”&#xff08;socket&#xff09;的组件我们基于这个组件进行网络通信开发。tcp套接字工作流程都以“打电话”来生…

python异常

一.什么是异常 异常是一个事件&#xff0c;该事件会在程序执行过程中发生&#xff0c;会影响程序的正常运行。一般情况下&#xff0c;python无法正常处理一个异常&#xff0c;会导致程序中断。在出现异常时&#xff0c;为了不影响程序的正常运行&#xff0c;我们需要捕获异常。…

HTML+JavaScript+CSS DIY 分隔条splitter

一、需求分析 现在电脑的屏幕越来越大&#xff0c;为了利用好宽屏&#xff0c;我们在设计系统UI时喜欢在左侧放个菜单或选项面板&#xff0c;在右边显示与菜单或选项对应的内容&#xff0c;两者之间用分隔条splitter来间隔&#xff0c;并可以通过拖动分隔条splitter来动态调研…

vue3 ref reactive响应式数据 赋值的问题

文章目录 vue3 ref reactive响应式数据 赋值的问题场景1:将响应式数据赋值请求后的数据错误示范&#xff1a;直接赋值正确写法 场景2&#xff1a;响应式数据解构之后失去响应式原因分析解决办法 toRefs/toRef方法创建ref引用对象 vue3 ref reactive响应式数据 赋值的问题 doing…

DBeaver 23.1.5 发布

导读DBeaver 是一个免费开源的通用数据库工具&#xff0c;适用于开发人员和数据库管理员。DBeaver 23.1.5 现已发布&#xff0c;更新内容如下. Data editor 重新设计了词典查看器面板 UI 空间数据类型&#xff1a;曲线几何线性化已修复 数据保存时结果选项卡关闭的问题已解决…

gitee上传本地项目bug

&#x1f92e;这个破bug不知道浪费了多长时间&#xff0c;以前没有记录&#xff0c;每次都忘记&#xff0c;这次记下来 问题描述 gitee创建仓库&#xff0c;然后根据它提示的如下命令&#xff0c;但一直报错 原因分析&#xff1a; 把命令复制出来&#xff0c;粘贴到Sublime …

vscode 清除全部的console.log

在放页面的大文件夹view上面右键点击在文件夹中查找 console.log.*$ 注意&#xff1a;要选择使用正则匹配 替换为 " " (空字符串)

gitlab-rake gitlab:backup:create 执行报错 Errno::ENOSPC: No space left on device

gitlab仓库备份执行 gitlab-rake gitlab:backup:create报错如下&#xff1a; 问题分析&#xff1a;存储备份的空间满 解决方法&#xff1a; 方法1&#xff1a;清理存放路径&#xff0c;删除不需要文件&#xff0c;释放空间。 方法2&#xff1a;创建一个根目录的挂载点&#x…

在Visual Studio 2017上配置并使用OpenGL

1 在Visual Studio 2017上配置并使用OpenGL 在GLUT - The OpenGL Utility Toolkit&#xff1a;GLUT - The OpenGL Utility Toolkit中点击“GLUT for Microsoft Windows 95 & NT users”&#xff0c;选择“If you want just the GLUT header file, the .LIB, and .DLL file…

推进数据要素化,数据云为何是“加速器”?

数据要素化&#xff0c;一个世界性难题。 相比于传统生产要素&#xff0c;数据要素具有获得非竞争性、使用非排他性等独有特征&#xff0c;在流通、产权、安全和使用等方面需要法规制度与基础设施的双重保障。 我国无疑是最早探索数据要素化的国家之一。从早期成立的各种大数…

Streamlit 讲解专栏(十一):数据可视化-图表绘制详解(中)

文章目录 1 前言2 绘制交互式散点图3 定制图表主题4 增强数据可视化的交互性与注释步骤1步骤二 5 结语 1 前言 在上一篇博文《 Streamlit 讲解专栏&#xff08;十&#xff09;&#xff1a;数据可视化-图表绘制详解&#xff08;上&#xff09;》中&#xff0c;我们学习了一些关…

Qt5界面Qt Designer上添加资源图片后,ModuleNotFoundError: No module named ‘rcc_rc‘ 的终极解决方案

在网上找了很久都没弄明白&#xff0c;最后还是自己思考解决了。 起因&#xff1a; 用 Qt Designer 添加资源文件作为背景图&#xff0c;编译 \resource\static\qrc> pyuic5 -o .\xx.py .\xx.ui发现在 xx.py 文件末尾中多了一个语句&#xff1a; import rcc_rc然后运行就…

Excel:通过Lookup函数提取指定文本关键词

函数公式&#xff1a;LOOKUP(9^9,FIND($G 2 : 2: 2:G 6 , C 2 ) , 6,C2), 6,C2),G 2 : 2: 2:G$6) 公式解释&#xff1a; lookup第一参数为9^9&#xff1a;代表的是一个极大值的数据&#xff0c;查询位置里面最接近这一个值的数据&#xff1b;lookup第二参数用find函数代替&am…

嵌入式开发-串口通信介绍

串口通信&#xff08;Serial Communications&#xff09;是一种串行数据传输方式&#xff0c;它将数据一位一位地顺序传输&#xff0c;而不是并行传输。这种方式相比并行传输更为节省空间和成本&#xff0c;因此在现代工业和嵌入式系统中得到广泛应用。 串口通信通常使用RS-23…

POI-TL制作word

本文相当于笔记&#xff0c;主要根据官方文档Poi-tl Documentation和poi-tl的使用&#xff08;最全详解&#xff09;_JavaSupeMan的博客-CSDN博客文章进行学习&#xff08;上班够用&#xff09; Data AllArgsConstructor NoArgsConstructor ToString EqualsAndHashCode public …

导入excel数据给前端Echarts实现中国地图-类似热力图可视化

导入excel数据给前端Echarts实现中国地图-类似热力图可视化 程序文件&#xff1a; XinqiDaily/frontUtils-showSomeDatabaseonMapAboutChina/finalproject xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com) https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOr…

OpenCV基本操(IO操作,读取、显示、保存)

图像的IO操作&#xff0c;读取和保存方法 1.1 API cv.imread()参数&#xff1a; 要读取的图像 读取图像的方式&#xff1a; cv.IMREAD*COLOR:以彩色模式加载图像&#xff0c;任何图像的图像的透明度都将被忽略。这是默认参数 标志&#xff1a; 1 cv.IMREAD*GRAYSCALE :以…

WebVR — 网络虚拟现实

推荐&#xff1a;使用 NSDT编辑器 快速搭建3D应用场景 虚拟现实设备 随着Oculus Rift和许多其他生产设备即将上市&#xff0c;未来看起来很光明——我们已经有足够的技术来使VR体验“足够好”&#xff0c;可以玩游戏。有许多设备可供选择&#xff1a;像Oculus Rift或HTC Vive这…

不用订阅,不用破解,永久免费使用Axure最新版教程

首先去官网下载最新的axure&#xff0c;你没听错&#xff0c;就是最新的。 下载网址&#xff1a;Axure RP - UX Prototypes, Specifications, and Diagrams in One Tool 下载完后解压安装到本地&#xff0c;并注册属于你自己的账户&#xff0c;开始试用。可惜的是只有30天的试…

C++:类和对象(下)

目录 1. 再谈构造函数 1.1构造函数题赋值 1.2初始化列表 初始化列表有什么用呢? 1.3 explicit关键字 2. Static成员 2.1概念 2.2特性 3. 友元 3.1友元函数 3.2友元类 4. 内部类(了解) 5.匿名对象 6.拷贝对象时的一些编译器优化 学习目标 1. 再谈构造函数2. Stat…