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

這也就再一次印證了我們的結論😁。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165451.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165451.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165451.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

旅行商問題(枚舉,回溯,動態規劃,貪心,分支界限)

文章目錄 問題描述暴力枚舉回溯法動態規劃法貪心法分支界限法 問題描述 假設有一個貨郎擔要拜訪n個城市&#xff0c;他必須選擇所要走的路程&#xff0c;路程的限制時每個城市只能拜訪一次&#xff0c;而且最后要走到原來出發的城市&#xff0c;要求路徑長度。 旅行商問題將要…

為銷售賦能:利用 Splashtop 增強遠程培訓技術

遠程銷售團隊這一概念在當今快節奏的商業環境中日益普遍。各公司正在計劃在不同地點靈活開展銷售業務&#xff0c;希望利用技術優勢縮小地域差距。但是&#xff0c;這種向遠程銷售的轉型面臨著重大挑戰&#xff0c;尤其在培訓和發展領域。培訓遠程銷售團隊需要采用創新方法&…

常見樹種(貴州省):012茶、花椒、八角、肉桂、杜仲、厚樸、枸杞、忍冬

摘要&#xff1a;本專欄樹種介紹圖片來源于PPBC中國植物圖像庫&#xff08;下附網址&#xff09;&#xff0c;本文整理僅做交流學習使用&#xff0c;同時便于查找&#xff0c;如有侵權請聯系刪除。 圖片網址&#xff1a;PPBC中國植物圖像庫——最大的植物分類圖片庫 一、茶 灌…

鴻蒙 ark ui 輪播圖實現教程

前言&#xff1a; 各位同學有段時間沒有見面 因為一直很忙所以就沒有去更新博客。最近有在學習這個鴻蒙的ark ui開發 因為鴻蒙不是發布了一個鴻蒙next的測試版本 明年會啟動純血鴻蒙應用 所以我就想提前給大家寫一些博客文章 效果圖 具體實現 我們在鴻蒙的ark ui 里面列表使…

土地利用數據技術服務

一、背景介紹 土地是人類賴以生存與發展的重要資源和物質保障&#xff0c;在“人口&#xff0d;資源&#xff0d;環境&#xff0d;發展&#xff08;PRED&#xff09;”復合系統 中&#xff0c;土地資源處于基礎地位。隨著現代社會人口的不斷增長以及工業化、城市化進程的加速&a…

Excel使用VLOOKUP查詢數據

VLOOKUP函數在百度百科中的解釋是&#xff1a; 解釋一下&#xff0c;函數需要4個參數&#xff1a; 參數1&#xff08;lookup_value&#xff09;&#xff1a;需要匹配的值參數2&#xff08;table_array&#xff09;&#xff1a;在哪個區域里進行匹配參數3&#xff08;col_index…

Dubbo3使用Zookeeper作為注冊中心的方案討論!詳解DubboAdmin與PrettyZoo來監控服務的優劣!

文章目錄 一&#xff1a;Dubbo注冊中心的基本使用 二&#xff1a;Zookeeper注冊中心的使用 1&#xff1a;依賴引入 2&#xff1a;實際開發 三&#xff1a;Zookeeper作為注冊中心的使用展示 1&#xff1a;啟動注冊Zookeeper服務 2&#xff1a;引入注冊中心 (一)&#xf…

Java 21增強對Emoji表情符號的處理了

現一個 Java 21 中有意思的東西&#xff01; 在java.Lang.Character類中增加了用于確定字符是否為 Emoji 表情符號的 API&#xff0c;主要包含下面六個新的靜態方法&#xff1a; public static boolean isEmoji(int codePoint) {return CharacterData.of(codePoint).isEmoji(…

操作系統 day13(RR、優先級調度)

RR&#xff08;時間片輪轉&#xff09; 響應時間&#xff1a;系統中有10個進程正在并發執行&#xff0c;如果時間片為1秒&#xff0c;則一個進程被響應可能需要等待9秒。也就是說&#xff0c;如果用戶在自己進程的時間片外通過鍵盤發出調試命令&#xff0c;可能需要等待9秒才能…

中斷方式的數據接收

中斷接收簡介 回顧之前的代碼 之前的代碼是 等待標志位RXNE位為1才有數據 進而讀取數據存放在變量c中 再根據c變量的數據是為0還是為1進而編寫燈亮滅的代碼 if語句 但這樣的代碼明顯不符合裸機多任務的編程模型 因為在while中為進程 進程執行的時間不能大于5ms 但是while&…

Qt/QML編程學習之心得:一個Qt工程的學習筆記(九)

1、.pro文件 加CONFIG += c++11,才可以使用Lamda表達式(一般用于connect的內嵌槽函數) 2、QWidget 這是Qt新增加的一個類,基類,窗口類,QMainWindow和QDialog都繼承與它。 3、Main函數 QApplication a應用程序對象,有且僅有一個 a.exec() 進行消息循環、阻塞 MyWi…

《圖解Java數據結構與算法:微課視頻版》簡介

本書系統、全面地介紹數據結構的基礎理論與算法設計&#xff0c;精選數據結構考研習題和各類典型例題進行講解&#xff0c;案例和課后習題豐富&#xff0c;突出對數據結構算法實踐能力的培養。本書算法均采用Java語言實現&#xff0c;示例代碼可直接上機運行。 本書配套資源豐…

Spring-jdbcTemplate-配置數據庫連接池,配置文件方式beans.xml

1、jdbc.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql:///studb jdbc.userroot jdbc.pwd123456 2、beans.xml <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans&…

Python BDD 框架比較之 pytest-bdd vs behave

pytest-bdd和behave是 Python 的兩個流行的 BDD 測試框架&#xff0c;兩者都可以用來編寫用戶故事和可執行的測試用例&#xff0c; 具體選擇哪一個則需要根據實際的項目狀況來看。 先簡單看一下兩者的功能&#xff1a; pytest-bdd 基于pytest測試框架&#xff0c;可以與pytest…

港口大型設備狀態監測及預測性維護策略

在現代港口運營中&#xff0c;大型設備的正常運行對于保障港口作業的高效性至關重要。為了實現設備的可靠性和持續性&#xff0c;港口管理者需要采取一系列狀態監測和預測性維護策略。 推進自動化和智能化是提高港口大型設備狀態監測和維護管理效率的重要途徑。通過應用先進的…

【計算機網絡筆記】數據鏈路層概述

系列文章目錄 什么是計算機網絡&#xff1f; 什么是網絡協議&#xff1f; 計算機網絡的結構 數據交換之電路交換 數據交換之報文交換和分組交換 分組交換 vs 電路交換 計算機網絡性能&#xff08;1&#xff09;——速率、帶寬、延遲 計算機網絡性能&#xff08;2&#xff09;…

讀像火箭科學家一樣思考筆記07_探月思維

1. 挑戰“不可能”的科學與企業 1.1. 互聯網 1.1.1. 和電網一樣具有革命性&#xff0c;一旦你插上電源&#xff0c;就能讓自己的生活充滿活力 1.1.2. 互聯網的接入可以幫助人們擺脫貧困&#xff0c;拯救生命 1.1.3. 互聯網還可以提供與天氣相關的信息 1.2. 用廉價、可靠的…

Windows如何截取屏幕圖片以及動態圖

在制作PPT或是其他演示文稿或是說明文檔的時候&#xff0c; 常常需要截取網頁或是屏幕的截圖&#xff0c;在Windows中有多種方式可以實現截取屏幕。 Windows 截取屏幕圖片的方式 在Windows 中截取屏幕中某個區塊的方式有&#xff1a; 方式1. 最原始的方式&#xff1a; 點擊 …

C練習題_2

一、單項選擇題(本大題共20小題,每小題2分,共40分。在每小題給出的四個備選項中選出一個正確的答案&#xff0c;并將所選項前的字母填寫在答題紙的相應位置上。&#xff09; 以下敘述中錯誤的是&#xff08;) A.對于double類型數組&#xff0c;不可以直接用數組名對數組進行整…

機器學習與藥物篩選的心得體會

機器學習在藥物設計里面的應用可以說還是比較常見的&#xff0c;尤其是搞計算的都會或多或少的涉及到這塊。比如國內做這塊比較多的&#xff0c;浙江大學的侯廷軍教授&#xff0c;北京化工大學的閆愛霞教授&#xff0c;華東理工大學的幾個做模擬計算的老師&#xff0c;上海藥物…