React 18 在组件间共享状态

news2024/11/14 14:40:40

参考文章

在组件间共享状态

有时候,希望两个组件的状态始终同步更改。要实现这一点,可以将相关 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 向下传递 来更改其状态:

<>
  <Panel
    isActive={activeIndex === 0}
    onShow={() => setActiveIndex(0)}
  >
    ...
  </Panel>
  <Panel
    isActive={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>
      <Panel
        title="关于"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        阿拉木图人口约200万,是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。
      </Panel>
      <Panel
        title="词源"
        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.coloradmin.cn/o/948198.html

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

相关文章

记一次坑爹的ARouter::There is no route match the path

接入阿里的ARouter框架&#xff1b;实现最简单的页面跳转&#xff1b;一直失败&#xff0c;报找不到匹配的路径&#xff1b;百度各种基本都说配置有问题&#xff1b;我一一对应&#xff0c;配置怎么看看不出问题来&#xff1b; 我的架构是 接入的代码怎么看都没问题&#xff1…

高并发幂等计数器的设计与实现

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

react css 污染解决方法

上代码 .m-nav-bar {background: #171a21;.content {height: 104px;margin: 0px auto;} }import React from "react"; import styles from ./css.module.scssexport default class NavBar extends React.Component<any, any> {constructor (props: any) {supe…

Linux常用命令——dd命令

在线Linux命令查询工具 dd 复制文件并对原文件的内容进行转换和格式化处理 补充说明 dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的&#xff0c;对于一些比较底层的问题&#xff0c;使用dd命令往往可以得到出人意料的效果。用的比较多的还是…

[ZenTao]源码阅读:加载自定义任务类型

www/index.php config/config.php framework/base/router.class.php tmp/model/common.php module/common/model.php framework/router.class.php

《动手学深度学习》-55循环神经网络

沐神版《动手学深度学习》学习笔记&#xff0c;记录学习过程&#xff0c;详细的内容请大家购买书籍查阅。 b站视频链接 开源教程链接 循环神经网络 潜变量自回归模型&#xff1a; 循环神经网络结构&#xff1a; 简单来说循环神经网络RNN就是在MLP中加了一项&#xff0c;使它可…

气传导蓝牙耳机哪个好?好用爆款气传导蓝牙耳机推荐

​对于气传导耳机&#xff0c;还有很多朋友觉得还是比较陌生&#xff0c;气传导工作原理是通过空气传播&#xff0c;由耳廓收集声音&#xff0c;然后以声波的形式引起鼓膜振动。气传导耳机除了拥有骨传导耳机优点之外&#xff0c;长时间佩戴没有震麻感&#xff0c;音质比骨传导…

alibabacloud的简单使用,nacos配置中心+服务中心。作者直接给自己写的源码

文章目录 依赖关键主要的程序启动文件配置文件bootstrap.yml依赖文件nacos配置中心上的文件截图 启动成功截图参考文档 依赖关键 SpringBoot版本和com.alibaba.cloud版本需要对应&#xff0c;不然会程序会启动失败作者使用的版本 SpringBoot: 2.1.6.RELEASE alibabacloud: 2.…

Android AGP8.1.0组件化初探

Android AGP8.1.0组件化初探 前言&#xff1a; 前面两篇完成了从AGP4.2到 AGP8.1.0的升级&#xff0c;本文是由于有哥们留言说在AGP8.0中使用ARouter组件化有问题&#xff0c;于是趁休息时间尝试了一下&#xff0c;写了几个demo&#xff0c;发现都没有问题&#xff0c;跳转和传…

循环的技巧和深入条件控制

这里对深入条件控制的知识点做一下测试&#xff1a;用作普通值而不是布尔值时&#xff0c;短路运算符的返回值通常是最后一个求了值的参数。 a2c5 for b in [0,1]:print((a and b and c))运行结果 E:\Python\Python38\python.exe D:/pythonprojects/python-auto-test/test/ti…

【MySQL】mysql connect

目录 一、准备工作 1、创建mysql用户 2、删除用户 3、修改用户密码 3.1、自己改自己密码 3.2、root用户修改指定用户的密码 4、数据库的权限 4.1、给用户授权 4.2、回收权限 二、连接mysql client 1、安装mysql客户端库 2、验证是否引入成功 三、 mysql接口 1、初…

Spring boot 整合 Okhttp3 并封装请求工具

一、 为什么要使用okHttp OkHttp是一个高效、灵活、易于使用的HTTP客户端库&#xff0c;优势如下&#xff1a; 性能更高&#xff1a;OkHttp在网络请求处理上采用了异步模型&#xff0c;并将连接池、压缩、网络协议等多种技术应用到其中&#xff0c;从而提高了网络请求的效率和…

c语言每日一练(12)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

MongoDB实验——在MongoDB集合中查找文档

在MongoDB集合中查找文档 一、实验目的二、实验原理三、实验步骤1.启动MongoDB数据库、启动MongoDB Shell客户端2.数据准备-->person.json3.指定返回的键4 .包含或不包含 i n 或 in 或 in或nin、$elemMatch&#xff08;匹配数组&#xff09;5.OR 查询 $or6.Null、$exists7.…

Vue3 学习 组合式API setup语法糖 响应式 指令 DIFF(一)

文章目录 前言一、Composition Api二、setup语法糖三、响应式refreactive 四、其他一些关键点v-prev-oncev-memov-cloak 五、虚拟Dom五、diff算法 前言 本文用于记录学习Vue3的过程 一、Composition Api 我觉得首先VUE3最大的改变就是对于代码书写的改变&#xff0c;从原来选择…

《自然语言处理》chapter7-预训练语言模型

这是阅读《自然语言处理-基于预训练模型的方法》的学习笔记&#xff0c;记录学习过程&#xff0c;详细的内容请大家购买书籍查阅。 同时参考沐神的两个视频&#xff1a; GPT&#xff0c;GPT-2&#xff0c;GPT-3 论文精读【论文精读】 BERT 论文逐段精读【论文精读】 概述 自然…

如何保证跨境传输的安全性?

随着互联网时代的到来&#xff0c;全球文件传输频率不断增加&#xff0c;市场经济的发展也对信息共享提出更高要求。传统电话交流已无法满足跨国企业的需求&#xff0c;企业内部诸如Web、电子邮件、企业资源计划&#xff08;ERP&#xff09;、网络电话&#xff08;VOIP&#xf…

SAP ABAP 代码调优检查工具及性能调优

一&#xff1a;代码检查工具 ABAP 测试仪表盘(ATC) 所有检查工具, 豁免处理, 结果存储的中心 代码检查器 (SCI) 提供给客户&#xff0c;合作伙伴和SAP的做代码相关检查的开放式架构 扩展程序检查(SLIN) 扩展的代码检查&#xff0c;用来分析源代码 SAP NetWeaver 应用服务器&a…

VMware虚拟机网络连接设置——NAT模式(Windows版)

首先参考VMware虚拟机网络连接设置——仅主机模式&#xff08;Windows版&#xff09;_vmware仅主机模式_Mr.LiuZB的博客-CSDN博客配置&#xff0c;网络还是不通&#xff0c;再结合Linux 虚拟机和主机互通 [万能方法]_linux虚拟机与主机网络连接_核桃胡子的博客-CSDN博客 配置&…

AD域中批量添加域用户

首先在C盘中建立一个文件&#xff0c;名字为file.csv 格式如下 根据CSV文件的ABCDE列来进行识别的 然后我们在cmd命令行中输入一下命令 for /f "tokens1,2,3,4,5 delims," %a in (C:file.csv) do dsadd user "cn%c,ou业务部,ou博迈科技,dcBMKJ,dccom" -s…