当前位置: 首页 > news >正文

React16中打印事件对象取不到值的现象及其原因分析

React16中打印事件对象取不到值的现象及其原因分析

一、背景

在最近的开发过程中,遇到了一个看起来匪夷所思的问题❓:

<Inputplaceholder="请输入"onChange={(e) => {console.log('e:', e)}}onKeyDown={handleKeyDown} />

此时按理来说我们只要拿到e.target.value就可以拿到输入框中的值,我们在输入框中输入1进行测试,然而打印出来却是这样的😥:

image-20231122114133673

我们拿到的target是个null,而且事件对象e的各个属性都是以(...)的形式出现的,点开以后可以发现居然全是空的😨:

image-20231122114514991

这便引出了第一个问题,但是还没完,更邪门的问题还在后面🤔:

<Inputplaceholder="请输入"onChange={(e) => {console.log('e.target.value:', e.target.value)console.log('e:', e)	}}onKeyDown={handleKeyDown} />

这段代码相比较于上一段代码,只是多打印了一个e.target.value,从上面的经验来看,既然e.target是空的,那么继续访问它的value应该会报错才对,我们再次在输入框中输入1进行测试,然而结果再次出乎意料🤯:

image-20231122115044122

直接打印e.target.value居然能拿到输入的1 🤔,这便是我们的第二个问题

二、问题1:事件对象e的属性为何为空?

要回答这个问题,需要引出我们的标题——‘事件对象池’

1.合成事件 – onXxx={函数}

在介绍事件对象池之前我们需要先简单介绍下合成事件(SyntheticEvent)的概念,简单来说就是在 React 中基于onXxx={函数}的事件处理函数时,React 会将原生 DOM 事件封装成一个合成事件对象,然后传递给事件处理函数。这个合成事件对象包含了与原生 DOM 事件相同的信息,以及一些额外的属性和方法,用于处理事件。

至于合成事件更具体细致的内容,不是本文的重点,在这里只需要先简单的认识到这两点:

  • react中的合成事件和我们平时写的原生事件并不是同一个东西
  • 合成事件基于事件委托实现,在react16中委托给document

即可。

如果想要更加细致地学习合成事件相关的知识,这里笔者推荐观看b站上珠峰React视频中(https://www.bilibili.com/video/BV1sx4y1L7Rg?p=21&vd_source=9e266526041bdf6f2a69b06653a7eb54)P21-P25。 这个视频虽然时间较长,但讲解较为细致。

或者可以自行寻找一些博客或者文档学习这一部分的知识。

2.事件对象池

为了避免每次事件触发都需要重新创建新的合成事件对象,React在16版本中引入了“事件对象池”缓存机制。具体来说,当每次事件触发传播到委托的元素上时,React会统一处理内置事件对象生成合成事件对象。然后,React从事件对象池中获取存储的合成事件对象,并把信息赋值给相关的成员。等待本次操作结束后,React会把合成事件对象中的成员信息清空,并放回事件对象池中等待下一次使用。这种机制避免了重复创建和销毁大量的合成事件对象,从而提高了React的性能和效率。

这里的关键点就是在于——React会把合成事件对象中的成员信息清空

image-20231122143745048

同时值得注意的是,在 React 17 及其后续版本中,React 已经移除了事件池机制。

3.对问题1的初步解释

从上面的分析中我们可以得知,在react16版本中存在事件对象池的机制,在操作结束以后,react会把合成事件对象中的成员信息都清空掉,再放入到事件对象池当中。

这和我们之前 遇到的事件对象e的各个属性都是以(...)的形式出现的,点开以后可以发现居然全是空的 这一现象吻合😤。

image-20231122114514991

4.e.persist()

React 提供了 e.persist() 方法。e.persist() 方法的作用就是从事件对象池中移除合成事件对象的引用,使得事件对象在事件处理函数执行结束后仍然可用。它告诉 React 不要在事件处理函数执行完毕后重置合成事件对象。

这里给出react旧版本的官方文档中有关e.persist()的传送门:https://legacy.reactjs.org/docs/legacy-event-pooling.html#gatsby-focus-wrapper

由于在2023年的2月份,react的官方文档已经进行了一次大更新,整个文档都重写了一遍,目前的新文档全面拥抱hooks,同时点击老文档的链接会直接重定向到最新的文档,直接从搜索引擎搜索也很难找到老文档,这里建议有需要的朋友们可以收藏一下。

<Inputplaceholder="请输入"onChange={(e) => {e.persist()console.log('e:', e)}}/>

此时我们再次输入1 ,可以观察到此时的事件对象并没有消失,各个成员信息都回来了😋!

image-20231122173437129

到这里,问题1看似已经得到了解决,但又怎么解释问题2的现象呢🤔?

三、问题2:直接打印e.target.value为什么能拿到输入的1

1.问题分析

<Inputplaceholder="请输入"onChange={(e) => {console.log('e.target.value:', e.target.value)console.log('e:', e)	}}/>

这是我们之前的代码,我们发现e.target.value 居然有值,而e中展开后e.target 却为null,按理说事件对象e被回收后应该是拿不到值的啊,这就很奇怪了。

难道是代码执行顺序的问题,因为是先打印的e.target.value 后打印的 e ,但是这两行代码紧紧挨在一起,执行的时间很短啊,事件对象池不会这么凑巧在这两行代码执行的间隙中生效回收事件对象吧。

让我们将它们调换一下顺序试试:

<Inputplaceholder="请输入"onChange={(e) => {console.log('e:', e)console.log('e.target.value:', e.target.value)}}/>

image-20231122175238779

image-20231122175246331

结果先执行的e中target依然为null,而后执行的e.target.value 中居然能拿到值!

这个问题笔者也困扰了好久,哪怕是去上网搜索或者借助ai工具也很难得到一个合理的解释😭。

2.console.log() 的机制

就在笔者几乎是要放弃的时候,突然想到,这两个都是通过console.log() 来打印的,那会不会是因为console.log()存在某种机制,比如说输出时机上的一些讲究,才导致了这一现象呢,只不过是console.log() 实在是太常用了,才导致我们疏忽了。

进入mdn文档查找后,果然是console.log() 的原因!🤯

mdn文档中有关console.log() 的部分 : https://developer.mozilla.org/zh-CN/docs/Web/API/console/log_static

image-20231122181045295

也就是说当 console.log() 打印对象时,存在着一套特殊的机制,它将得到该对象的引用,而且输出的并不是在执行console.log() 时的值,而是打开控制台时的值。

我们用一段简单的代码来验证一下这个情况:

<Inputplaceholder="请输入"onChange={(e) => {const obj = { a: '1' }console.log('obj1:', obj)obj.a = '2'}}
/>

尽管看起来obj.a还是1,但是展开以后已经变成了2

image-20231123093956954

甚至我们可以更大胆的尝试:

<Inputplaceholder="请输入"onChange={(e) => {const obj = { a: '1' }console.log('obj1:', obj)obj.a = '2'setTimeout(() => {obj.a = '2'}, 500)}}
/>

image-20231123094139488

得到的依然是同一个结果😎

四、总结

1.结论

根据上述讨论,我们的逻辑可以得到合理的闭环:

<Inputplaceholder="请输入"onChange={(e) => {console.log('e:', e)console.log('e.target.value:', e.target.value)}}/>

以这段代码为例,当我们在输入框中输入值的时候,先后触发了

console.log('e:', e)
console.log('e.target.value:', e.target.value)

这两句代码的执行。

在执行第一个 console.log('e:', e) 时,由于e是个对象,根据console.log() 的机制,console.log此时将得到该对象的引用;

在执行第二个 console.log('e.target.value:', e.target.value) 时,由于e.target.value 并不是对象, console.log() 此时直接得到这个值。

接着等到我们打开控制台时,由于此时的事件对象e已经被事件对象池 回收了,因此打印出的第一项e中的各个成员信息都是空的,而第二项中的 e.target.value 并不是对象,因此能直接打印出它的值。

2.利用debugger工具来验证

我们再利用debugger工具来验证我们的想法:

<Inputplaceholder="请输入"onChange={(e) => {console.log('e:', e)console.log('e.target.value:', e.target.value)debugger}}/>

通过debugger可以看到,代码在执行到 console.log() 的时候,此时是有值的:
image-20231123091249407

这也就再一次印证了我们的结论😁。

相关文章:

React16中打印事件对象取不到值的现象及其原因分析

React16中打印事件对象取不到值的现象及其原因分析 一、背景 在最近的开发过程中&#xff0c;遇到了一个看起来匪夷所思的问题❓&#xff1a; <Inputplaceholder"请输入"onChange{(e) > {console.log(e:, e)}}onKeyDown{handleKeyDown} />此时按理来说我…...

绝对干货-讲讲设计模式之创建型设计模式的本质

创建型模式&#xff08;Creational Patterns&#xff09;&#xff1a;创建型模式关注对象的创建机制&#xff0c;包括了如何实例化一个对象或者一组对象的方法。Java中的创建型模式有&#xff1a;单例模式&#xff08;Singleton Pattern&#xff09;、工厂模式&#xff08;简单…...

机器人规划算法——movebase导航框架源码分析

这里对MoveBase类的类成员进行了声明&#xff0c;以下为比较重要的几个类成员函数。 构造函数 MoveBase::MoveBase | 初始化Action 控制主体 MoveBase::executeCb收到目标&#xff0c;触发全局规划线程&#xff0c;循环执行局部规划 全局规划线程 void MoveBase::planThread |…...

Android:Google三方库之Firebase集成详细步骤(三)

Cloud Messaging 1、清单文件配置 a、&#xff08;可选&#xff09;一项扩展 FirebaseMessagingService 的服务。除了接收通知外&#xff0c;如果您还希望在后台应用中进行消息处理&#xff0c;则必须添加此服务。例如&#xff0c;您需要在前台应用中接收通知、接收数据载荷以及…...

2023年中国边缘计算网关现状及发展趋势分析[图]

边缘计算网关是一种可以在设备上运行本地计算、消息通信、数据缓存等功能的工业智能网关&#xff0c;可以在无需联网的情况下实现设备的本地联动以及数据处理分析。边缘计算网关是一种连接物联网设备和云端服务的关键技术&#xff0c;它可以在设备和云端之间建立一个安全、高效…...

LeetCode78.子集

这道题如果用暴力法几乎是不可能解出来的&#xff0c;因为情况太复杂了&#xff0c;但是一旦用上递归回溯就会轻松很多&#xff0c;先上代码&#xff1a; class Solution {List<List<Integer>> result new ArrayList<List<Integer>>();List<Integ…...

不是默认进入Linux|总是自动进入windows系统

问题描述 不是默认进入Linux系统无法主动出现boot引导自动进入windows系统 尝试无效 修复引导无效重装Grub无效重装系统无效 环境 Ubuntu 22.04 LST微星主板 解决方案 修改引导顺序&#xff1a; 开机狂按Del键&#xff0c;进入BIOS系统&#xff0c;左侧Settings 设置&…...

【面经八股】搜广推方向:常见面试题(二)

【面经&八股】搜广推方向:常见面试题(二) 文章目录 【面经&八股】搜广推方向:常见面试题(二)1. FTRL 是什么?(Follow The Regularized Leader)2. 梯度下降方法3. 推荐系统中常见的Embedding方法有哪些?4. Embedding与推荐系统有哪些结合5. FM 和 FFM6. FNN7. 深…...

机器学习与药物筛选的心得体会

机器学习在药物设计里面的应用可以说还是比较常见的&#xff0c;尤其是搞计算的都会或多或少的涉及到这块。比如国内做这块比较多的&#xff0c;浙江大学的侯廷军教授&#xff0c;北京化工大学的闫爱霞教授&#xff0c;华东理工大学的几个做模拟计算的老师&#xff0c;上海药物…...

初识数据结构

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 熬过了我们不想要的生活&#xf…...

【阿里云】图像识别 智能分类识别 增加网络控制功能点(三)

一、增加网络控制功能 实现需求TCP 心跳机制解决Soket异常断开问题 二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。 查看当前系统的TCP KeepAlive参数修改TCP KeepAlive参数 三、C语言实现TCP KeepAlive功能 四、setsockopt用于设置套接字选项的系…...

LeetCode 统计美丽子字符串 II【质因子分解,前缀和,哈希表】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

第一百八十一回 如何绘制阴影效果

文章目录 1. 概念介绍2. 使用方法2.1 SegmentedButton2.2 ButtonSegment 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 1. 概念介绍 我们在本章回中介绍的SegmentedButton组件是一种分段式按钮&#xff0c;它把多个按钮连接成一组显示&#xff0c;组内再对不同的按钮进…...

Qt5.15.2静态编译 VS2017 with static OpenSSL

几年前编译过一次Qt静态库:VS2015编译Qt5.7.0生成支持XP的静态库,再次编译,毫无压力。 一.环境 系统:Windows 10 专业版 64位 编译器:visual studio 2017 第三方工具:perl,ruby和python python用最新的3.x.x版本也是可以的 这三个工具都需要添加到环境变量,安装时勾选…...

AIGC(生成式AI)试用 13 -- 数据时效性

数据时效性&#xff1f; 最新的数据&#xff0c;代表最新的状态&#xff0c;使用最新的数据也应该最有说服力。 学习需要时间&#xff0c;AIGC学习并接收最新数据的效果如何&#xff1f; 问题很简单&#xff0c;如何验证&#xff1f;这个需要找点更新快的对像进行验证。…...

Sass的嵌套CSS 规则详细教程

文章目录 前言父选择器的标识符&群组选择器的嵌套子组合选择器和同层组合选择器&#xff1a;>、和~嵌套属性后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Sass和Less &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和…...

Spark---SparkCore(一)

一、术语与宽窄依赖 1、术语解释 1、Master(standalone):资源管理的主节点&#xff08;进程&#xff09; 2、Cluster Manager:在集群上获取资源的外部服务(例如&#xff1a;standalone,Mesos,Yarn) 3、Worker Node(standalone):资源管理的从节点(进程)或者说管理本机资源的…...

单链表原来是这样实现的!

文章目录 前言1. 链表的概念及结构1.1在链表里&#xff0c;每节“车厢”是什么样的呢&#xff1f;1.2为什么还需要指针变量来保存下⼀个节点的位置&#xff1f; 2. 单链表的实现1. 定义结构体(Seqlist)2. 打印函数(SLTPrint)小插曲&#xff0c;创建节点函数CreateNode3. 尾插函…...

excel一个单元格换行方法

要是在同一个单元格内输入文字输入不下的话&#xff0c;我们是可以进行同一个单元格换行设置的&#xff0c;而且换行的方法也是有很多种&#xff0c;下面我们就一起来看一下有哪些方法吧。 excel一个单元格换行方法&#xff1a; 方法一&#xff1a; 1、我们可以直接按下alte…...

echart一键生成迁徙图

echart_move 介绍 echart迁徙图&#xff0c;选择起点和目的地生成迁徙图 软件架构 html echarts js 使用说明 将文件放到同一目录下打开index.html即可 默认是小飞机图标&#xff0c;如果想修改图标&#xff0c;将图片放到同一目录&#xff0c;如1.svg 代码修改为对应位…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...