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

news2024/12/28 6:34:39

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

一、背景

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

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

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

image-20231122114133673

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

image-20231122114514991

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

<Input
  placeholder="请输入"
  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,同时点击老文档的链接会直接重定向到最新的文档,直接从搜索引擎搜索也很难找到老文档,这里建议有需要的朋友们可以收藏一下。

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

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

image-20231122173437129

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

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

1.问题分析

<Input
  placeholder="请输入"
  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 ,但是这两行代码紧紧挨在一起,执行的时间很短啊,事件对象池不会这么凑巧在这两行代码执行的间隙中生效回收事件对象吧。

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

<Input
  placeholder="请输入"
  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() 时的值,而是打开控制台时的值。

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

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

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

image-20231123093956954

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

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

image-20231123094139488

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

四、总结

1.结论

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

<Input
  placeholder="请输入"
  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工具来验证我们的想法:

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

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

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

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

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

相关文章

yoloV5模型中,x,s,n,m,l之间区别

避免误导大家,从小到大顺序为:n,s,m,l,x YOLOv5 的不同变体(如 YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x 和 YOLOv5n)表示不同大小和复杂性的模型。这些变体在速度和准确度之间提供了不同的权衡,以适应不同的计算能力和实时性需求。下面简要介绍这些变体的区别: YOLOv5s:这…

华清远见嵌入式学习——网络编程——作业4

作业要求&#xff1a;①使用IO多路复用中的select函数实现TCP并发服务器客户端 ②使用IO多路复用中的poll函数实现TCP并发服务器的服务器端 一、 代码 #include <myhead.h>#define SERPORT 8888 //服务器端口号 #define SERIP "192.168.114.113"…

python二叉树链树_树的链式存储结构

二叉链树是一种树状数据结构&#xff0c;其中每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。每个节点包含一个数据元素和指向其左右子节点的指针。二叉链树可以是空树&#xff0c;也可以是具有以下特点的非空树&#xff1a; 1. 每个节点最多有两个子节点。…

【AGC】集成AGC服务上架应用市场审核问题

【关键字】 AGC、应用市场、审核 【问题描述】 集成了AGC服务&#xff0c;上架到应用市场不通过&#xff0c;检查发现是com.huawei.secure.android.common.ssl.util.c.doInBackground 存在获取安装列表行为。 已经按照sdk 设置了&#xff0c;但是检测还是有授权前去获取安装列…

UniApp打包教程:使用HBuilder X和AppUploader完成原生App云打包和上架指南“

目录 uniapp进行打包 使用上架工具appuplode进行发包 1.登录appuploder软件 2.登陆开发者App Store后台 uniapp进行打包 在HBuilder X编辑器中打开需要打包的项目&#xff0c;然后点击上面菜单栏中 发行 > 原生App-云打包&#xff0c;对以下弹出的弹窗进行内容填写 填写完…

Modbus转Profinet网关连接PLC与天信流量计通讯案例

本文将为您详细介绍如何成功连接PLC与天信流量计&#xff1a;从选择合适的Modbus转Profinet网关开始&#xff0c;到设置网关以实现通讯连接&#xff0c;还会涵盖部署和故障排除过程中可能遇到的一些问题。 首先&#xff0c;选择合适的Modbus转Profinet网关至关重要。我们选用基…

《微信小程序开发从入门到实战》学习二十七

3.4 开发参与投票页面 3.4.2 借用伪造数据开发功能 为了便于开发&#xff0c;新建一个编译模式&#xff1a; 之前没看文章&#xff0c;每次都习惯性填完投票创建的信息提交再跳转看效果。好累。 添加变异模式开发真方便。 另外&#xff0c;点击提交后没跳转到投票页面&#…

Oracle中文显示???????解决办法

项目场景&#xff1a; Oracleoracle中文显示???解决办法 问题描述 原因分析&#xff1a; Oracle中文显示???通常是由于字符集不匹配或者编码问题导致的。当数据库中的数据使用的是某种字符集&#xff0c;而客户端或者应用程序使用的是另一种字符集时&#xff0c;就会出…

基于51单片机的病床呼叫系统设计

**单片机设计介绍&#xff0c; 基于51单片机的病床呼叫系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的病床呼叫系统是一种用于医疗机构的设备&#xff0c;旨在提供快速、可靠的病人呼叫和监控功能。以下是…

使用yum安装jdk,并配置环境变量

写在前面: yum安装的jdk&#xff0c;默认不用配置环境变量就可以java -version显示结果&#xff0c;但是却不能在系统环境变量中查看到JAVA_HOME&#xff0c;由于其他应用需要这个环境变量&#xff0c;比如hadoop&#xff0c;所以这里说明如何进行java的相关环境变量配置 1. y…

基于springboot实现实习管理系统的设计与实现项目【项目源码+论文说明】计算机毕业设计

基于sprinmgboot实现实习管理系统的设计与实现演示 摘要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;实习管理也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;…

11月23日星期四今日早报简报微语报早读

11月23日星期四&#xff0c;农历十月十一&#xff0c;早报微语早读。 1、我国5G基站总数达321.5万个&#xff1b; 2、2023年两院院士增选结果揭晓&#xff0c;共133人当选&#xff1b; 3、北京低保标准提升至每人每月1395元&#xff1b; 4、上海制定体育发展条例&#xff1a…

2023 年亚马逊黑色星期五和网络星期一的企业电子商务指南

亚马逊黑色星期五和网络星期一 周末即将到来&#xff01;感恩节于 11 月 23 日举行&#xff0c;紧接着是 24 日黑色星期五和 27 日网络星期一。您的亚马逊业务准备好应对大量涌入了吗&#xff1f; 我们相信您已经准备好黑色星期五优惠并准备好库存&#xff0c;以确保您有足够的…

小型洗衣机哪个牌子好用又耐用?最好用的迷你洗衣机

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…

在线接口测试工具fastmock使用

1、fastmock线上数据模拟器 在平时的项目测试中&#xff0c;尤其是前后端分离的时候&#xff0c;前端人员需要测试调用后端的接口&#xff0c;这个时候会出现测试不方便的情况。此时我们可以使用fastmock平台在线上模拟出一个可以调用的接口&#xff0c;方便前端人员进行数据测…

合伙人注册即得/创业平台扶持!

问心一语持续体验中~ 与公司签订线下&#xff08;线上&#xff09;纸质合伙人代理合同&#xff0c;具备法律效力。 一级直推、非一级直推注册即得奖励。(消耗完毕被邀请用户赠送的AI免费使用条数) 即&#xff1a;邀请1人注册即得并消耗&#xff01;只需注册无需充值&#xff…

YOLOV5标注训练自己的数据全流程教程

概述 yolo在目标检测领域是非常有代表性的模型&#xff0c;它速度快识别效果也很精准&#xff0c;是实时检测模型中应用最广泛的。yolo的原理和代码是很容易获得的&#xff0c;且有各式各样的教程&#xff0c;但是模型怎么使用的教程相对比较少。本文讲解如何使用yolov5模型训…

Tekton — 通过tekton-operator部署tekton组件

文章目录 版本信息部署准备安装卸载tekton组件 Tektoncd Operator 作为一个 Kubernetes 的扩展&#xff0c;可以方便快捷地在 Kubernetes 集群上安装、升级和管理 Tekton Pipelines、Dashboard、Triggers 等组件。 那么本篇文章介绍在K8S集群中如何通过tekton-operator部署Tekt…

基于单片机的空气质量实时监测系统(论文+源码)

1. 系统设计 通过文献和市场调查&#xff0c;本设计的实现方案框架是以单片机为核心控制处理器搭建外围的功能模块如温度传感器模块、湿度传感器检测模块、二氧化碳传感器检测设备模块、无线通信模块和蜂鸣器声光报警提示模块来实现&#xff0c;辅以显示模块来展示。 该系统通…