续篇:展开聊下 state 与 渲染树中位置的关系

🐾 上篇的结尾处,提到了 => 为了提升性能, React 仅在渲染之间 存在差异 时才会更改 DOM 节点。

本篇,✓ 🇨🇳 展开聊下 state 与 渲染树中位置的关系

📢📢📢 状态与渲染树中的位置相关

  • ✊ 相同位置的相同组件会使得 state 被保留下来
  • ✌️ 相同位置的不同组件会使 state 重置

只要一个组件还被渲染在 UI 树的相同位置,React 就会保留它的 state。 如果它被移除,或者一个不同的组件被渲染在相同的位置,那么 React 就会丢掉它的 state。

下述举例说明

// 子组件 Counter,用于记分
function Counter ({name}: { name: string }) {
    const [score, setScore] = useState(0);
    return (
        <div>
            <span>{name}</span>
            <p>Score: {score}</p>
            <button onClick={() => setScore(score + 1)}>加分</button>
        </div>
    )
}

状态与渲染树中的位置相关

React 通过组件在 渲染树中的位置将它保存的每个状态与正确的组件关联起来

export default () => {
    return (
        <>
            <Counter name="李刚"></Counter>
            <Counter name="奋飛"></Counter>
        </>
    )
}

在这里插入图片描述
这是两个独立的 counter,因为它们在树中被渲染在了各自的位置。

相同位置的相同组件会使得 state 被保留下来

name 由 “奋飛” 改为 “李刚”,记分器 state 并没有被重置!

export default () => {
    const [name, setName] = useState('奋飛');
    return (
        <>
            <input type="text"  value={name} onChange={(e: any) => setName(e.target.value)} />
            <Counter name={name}></Counter>
        </>
    )
}

在这里插入图片描述

它是 位于相同位置的相同组件,所以对 React 来说,它是同一个记分器。

⚠️ 对 React 来说重要的是组件在 UI 树中的位置,而不是在 JSX 中的位置!

React 不知道函数里是如何进行条件判断的,它只会“看到”返回的树。

export default () => {
    const [status, setStatus] = useState(true);
    return (
        <>
            <input type="checkbox" checked={status} onChange={(e: any) => setStatus(e.target.checked)} />
            {status ? <Counter name="奋飛"></Counter> : <Counter name="李刚"></Counter>}
        </>
    )
}

在这里插入图片描述

⚡ 勾选复选框的时候 state 未被重置,因为 两个 <Counter /> 标签被渲染在了相同的位置。

首次渲染
点击加分按钮
触发子组件渲染
初始化
父:status = true
子:score = 0
DOM
子组件
score改变
DOM
触发重新渲染
父组件
切换status
相同组件&&
相同UI位置
子组件
state被保留
DOM

⭐ 结论:通过上述的分析得知,一个组件被渲染在 UI 树的相同位置,React 就会保留它的 state。那么如何重置呢?

解决(state 重置)

  1. 使用不同的组件渲染
  2. 将组件渲染在不同的位置
  3. 使用 key 赋予每个组件一个明确的身份
方案1:使用不同的组件渲染
export default () => {
    const [status, setStatus] = useState(true);
    return (
        <>
            <input type="checkbox" checked={status} onChange={(e: any) => setStatus(e.target.checked)} />
            {status ? <div><Counter name="奋飛"></Counter></div> : <Counter name="李刚"></Counter>}
        </>
    )
}

第一个子组件从 div 变成了 Counter。当子组件 div 从 DOM 中被移除的时候,它底下的整棵树(包含 Counter 以及它的 state)也都被销毁了。

方案2:将组件渲染在不同的位置
export default () => {
    const [status, setStatus] = useState(true);
    return (
        <>
            <input type="checkbox" checked={status} onChange={(e: any) => setStatus(e.target.checked)} />
            {status && <Counter name="奋飛"></Counter>}
            {!status && <Counter name="李刚"></Counter>}
        </>
    )
}
  • 初始化 status 的值是 true:第一个位置是 Counter ,第二个位置是 的;
  • 切换 status 值为 false:第一个位置是 的 ,第二个位置是 Counter
方案3:使用 key1 赋予每个组件一个明确的身份
export default () => {
    const [status, setStatus] = useState(true);
    return (
        <>
            <input type="checkbox" checked={status} onChange={(e: any) => setStatus(e.target.checked)} />
            {status ? <Counter name="奋飛" key="fly"></Counter> : <Counter name="李刚" key="lg"></Counter>}
        </>
    )
}

指定一个 key 能够让 React 将 key 本身而非它们在父组件中的顺序作为位置的一部分。

‼️ key 不是全局唯一的。它们只能指定 父组件内部 的顺序。

延伸

不应该把组件函数的定义嵌套起来

export default function MyComponent() {
  const [counter, setCounter] = useState(0);

  function MyTextField() {
    const [text, setText] = useState('');

    return (
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
    );
  }

  return (
    <>
      <MyTextField />
      <button onClick={() => {
        setCounter(counter + 1)
      }}>点击了 {counter}</button>
    </>
  );
}

在这里插入图片描述
每次点击按钮,输入框的 state 都会消失!这是因为每次 MyComponent 渲染时都会创建一个 不同MyTextField 函数。

在相同位置渲染的是 不同 的组件,所以 React 将其下所有的 state 都重置了。

这样会导致 bug 以及性能问题。为了避免这个问题, 永远要将组件定义在最上层并且不要把它们的定义嵌套起来。

// 修复,将 MyTextField 组件抽离
function MyTextField() {
  const [text, setText] = useState('');

  return (
    <input
      value={text}
      onChange={e => setText(e.target.value)}
    />
  );
}

export default function MyComponent() {
  const [counter, setCounter] = useState(0);
	return (<> ... </>)
} 

  1. https://react.docschina.org/learn/rendering-lists#why-does-react-need-keys React 中为什么需要key? ↩︎

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java学习笔记之IDEA的安装与下载以及相关配置

1 IDEA概述 ​IDEA全称IntelliJ IDEA&#xff0c;是用于Java语言开发的集成环境&#xff0c;它是业界公认的目前用于Java程序开发最好的工具。 集成环境&#xff1a; ​把代码编写&#xff0c;编译&#xff0c;执行&#xff0c;调试等多种功能综合到一起的开发工具。 2 IDEA…

【视觉三维重建】【论文笔记】Deblurring 3D Gaussian Splatting

去模糊的3D高斯泼溅&#xff0c;看Demo比3D高斯更加精细&#xff0c;对场景物体细节的还原度更高&#xff0c;[官网]&#xff08;https://benhenryl.github.io/Deblurring-3D-Gaussian-Splatting/&#xff09; 背景技术 Volumetric rendering-based nerual fields&#xff1a…

GZ083 产品艺术设计赛题第五套

全国职业院校技能大赛 产品艺术设计赛项赛题五 赛项名称 产品艺术设计 英语名称 Product Art Design 赛项编号 GZ083 归属产业 数字产业 任务名称 “境•享”家用台式加湿器设计 赛项组别 中职组 高职组 □学生组 □教师组 □师生联队试点赛项 R学生组 □教师组…

【线程】封装 | 安全 | 互斥

线程封装&#xff08;面向对象&#xff09; 1.组件式的封装出一个线程类&#xff08;像C11线程库那样去管理线程&#xff09; 我们并不想暴露出线程创建&#xff0c;终止&#xff0c;等待&#xff0c;分离&#xff0c;获取线程id等POSIX线程库的接口&#xff0c;我们也想像C1…

机器学习模型—K最近邻(KNN)

机器学习模型—K最近邻(KNN) K最近邻 (KNN) 算法是一种用于解决分类和回归问题的监督机器学习方法。Evelyn Fix 和 Joseph Hodges 于 1951 年开发了该算法,随后 Thomas Cover 对其进行了扩展。本文探讨了 KNN 算法的基本原理、工作原理和实现。 虽然 k近邻算法 (KNN) 可以用…

基于Pytest+Allure+Excel的接口自动化测试框架

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发…

接口自动化测试实战之pytest框架+allure讲解

一、前言 本文章主要会讲解Python中pytest框架的讲解&#xff0c;介绍什么是pytest、为何要测试、为何使用以及参考和扩展等等&#xff0c;话不多说&#xff0c;咱们直接进入主题哟。 二、pytest讲解 2.1 什么是pytest&#xff1f; pytest是一款单元测试框架&#xff0c;在…

Linux -- 线程互斥

一 线程互斥的概念 大部分情况&#xff0c;线程使用的数据都是局部变量&#xff0c;变量的地址空间在线程栈空间内&#xff0c;这种情况&#xff0c;变量归属单个线程&#xff0c;其他线程无法获得这种变量。但有时候&#xff0c;很多变量都需要在线程间共享&#xff0c;这样的…

激光打标技术:现代制造业的精准标记解决方案

随着科技的飞速进步&#xff0c;激光打标机技术已经成为现代制造业中不可或缺的一部分。作为一种快速、精确、耐用的标记解决方案&#xff0c;激光打标技术以其独特的优势&#xff0c;为现代制造业提供了精准、高效、持久的标记解决方案。 首先&#xff0c;激光打标技术以其无与…

吴恩达机器学习笔记 十七 通过偏差与方差诊断性能 正则化 偏差 方差

高偏差&#xff08;欠拟合&#xff09;&#xff1a;在训练集上表现得也不好 高方差&#xff08;过拟合&#xff09;&#xff1a;J_cv要远大于J_train 刚刚好&#xff1a;J_cv和J_train都小 J_cv和J_train与拟合多项式阶数的关系 从一阶到四阶&#xff0c;训练集的误差越来越小…

挂耳式耳机什么牌子的好?掌握六大挂耳式耳机选购秘诀

随着科技的进步&#xff0c;蓝牙耳机逐渐成为人们日常生活中的热门配件。很多人选择蓝牙耳机&#xff0c;是为了在娱乐学习时享受便捷的无线体验。这些耳机不仅设计时尚&#xff0c;佩戴起来也极为舒适。 蓝牙耳机主要分为挂耳式和入耳式。尽管入耳式耳机功能齐全&#xff0c;…

在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?

目录 一、分布式寻址算法 1. hash 算法 2. 一致性 hash 算法 3. Redis cluster 的 hash slot 算法 二、Redis cluster 的高可用与主备切换原理 1. 判断节点宕机 2. 从节点过滤 3. 从节点选举 4. 与哨兵比较 一、分布式寻址算法 hash 算法(大量缓存重建) 一致性 hash…

Python的time模块与datetime模块大揭秘!

1.time 模块 t主要用来操作时间&#xff0c;还可以用于控制程序 导入time模块 import time 2.获取从1970年1月1日0时0分0秒距今的秒数&#xff1a;time.time() print(time.time()) 3.格式化显示时间&#xff1a;time.strftime() print(time.strftime("%Y-%m-%d %H:…

某赛通电子文档安全管理系统 DecryptApplication 任意文件读取漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

使用 Python+Selenium + 第三方库实现简单的web自动化测试框架 源码

一、配置(config) 1.1 说明 设置自动化案例运行时的属性值。 安排自动化案例的执行顺序。 所在路径&#xff1a; …\Project_Selenium\config 1.2 文件 1.2.1 config.ini 目录&#xff1a; …\Project_Selenium\config\config.ini 配置字段&#xff1a; 1 [PROJECT] 1.1 bro…

【C++那些事儿】深入理解C++类与对象:从概念到实践(下)| 再谈构造函数(初始化列表)| explicit关键字 | static成员 | 友元

&#x1f4f7; 江池俊&#xff1a;个人主页 &#x1f525; 个人专栏&#xff1a;✅C那些事儿 ✅Linux技术宝典 &#x1f305; 此去关山万里&#xff0c;定不负云起之望 文章目录 1. 再谈构造函数1.1 构造函数体赋值1.2 初始化列表1.3 explicit 关键字 2. static成员2.1 概念…

十四、Nacos源码系列:Nacos配置发布原理

目录 一、简介 二、加密处理 三、发布配置 3.1、插入或更新配置信息 3.2、发布配置数据变动事件 3.2.1、目标节点是当前节点 3.2.2、目标节点非当前节点 四、总结 一、简介 一般情况下&#xff0c;我们是通过Nacos提供的Web控制台登录&#xff0c;然后通过界面新增配置…

个人博客系列-后端项目-用户注册功能(7)

介绍 用户注册API的主要流程&#xff1a;1.前端用户提交用户名&#xff0c;密码 2. 序列化器校验用户名&#xff0c;密码是否合法。3.存入数据库。4.签发token 创建序列化器 from rest_framework import serializers from rest_framework_simplejwt.serializers import Toke…

图【数据结构】

文章目录 图的基本概念邻接矩阵邻接表图的遍历BFSDFS 图的基本概念 图是由顶点集合及顶点间的关系组成的一种数据结构 顶点和边&#xff1a;图中结点称为顶点 权值:边附带的数据信息 路径 &#xff1a; 简单路径 和 回路&#xff1a; 子图&#xff1a;设图G {V, E}和图G1…

计算机网络:关键性能指标与非性能特征解析

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…