React合成事件詳解
react合成事件指的是react用js模擬了一個(gè)Dom事件流。(fiber樹(shù)模擬Dom樹(shù)結(jié)構(gòu)) 合成事件的事件流在fiber樹(shù)中發(fā)生捕獲和冒泡。
從點(diǎn)擊輸入框開(kāi)始
當(dāng)你點(diǎn)擊input輸入框,react在根節(jié)點(diǎn)(注1)監(jiān)聽(tīng)到focus事件(注2)(注3)。
如何從原生事件找到對(duì)應(yīng)的虛擬Dom?
此時(shí),react得到的信息只有原生事件對(duì)象(nativeEvent)。react通過(guò)nativeEvent對(duì)應(yīng)的Dom(eventTarget),沿著Dom樹(shù)向上找到距離該eventTarget最近的被react管理的Dom節(jié)點(diǎn)(注4)(注5),并獲得對(duì)應(yīng)的fiber A。
接著通過(guò)事件插件(注6),創(chuàng)建合成事件(注7)A。合成事件A被react視為模擬事件流中的事件源,fiber A被react視為事件目標(biāo)。
合成事件流?
從fiber A出發(fā)向上(直到頂層fiber HostComponent為止)收集所有的host類(lèi)型的fiber。 然后將收集到的fiber數(shù)組,從后向前(捕獲),再?gòu)那跋蚝螅芭荩┍闅v。每次遍歷,會(huì)收集(注8)當(dāng)前遍歷項(xiàng)fiber節(jié)點(diǎn)的綁定的focus事件。之后(事件插件完成后,即合成事件生成好了)會(huì)按照收集的順序執(zhí)行foucs回調(diào)。 react就是這樣模擬了事件流。
擴(kuò)展
點(diǎn)擊輸入框引起多個(gè)事件
除了focus外,也觸發(fā)了其他的事件--click等。react在根節(jié)點(diǎn)對(duì)不同類(lèi)型的事件進(jìn)行了監(jiān)聽(tīng),每監(jiān)聽(tīng)到一種事件就會(huì)派發(fā)一次,多種類(lèi)型的事件,會(huì)派發(fā)多次。 點(diǎn)擊輸入框,會(huì)先后觸發(fā)focus和click。當(dāng)focus事件的派發(fā)完后,就會(huì)派發(fā)click事件。 每次某個(gè)事件派發(fā)結(jié)束,會(huì)處理待處理的同步任務(wù)隊(duì)列(flushSyncCallBackQueue)。
意料之外的render?
在NoMode模式下,多次派發(fā)事件且每個(gè)事件都改變了狀態(tài)(如調(diào)用setState),則對(duì)應(yīng)組件會(huì)被render多次。 在本例中,點(diǎn)擊input輸入框,如果給input綁定了focus事件和click事件并且事件回調(diào)都調(diào)用setState,input將會(huì)render兩次。
為什么react中的input設(shè)置value后成為受控組件?
react中,在input設(shè)置了value屬性的條件下,無(wú)論在輸入框中輸入什么,輸入框的值都不會(huì)改變。除非你改變了input組件的state。 react在處理完模擬事件流后,會(huì)調(diào)用方法將一些意外的效果重置。 例如該場(chǎng)景,在input中輸入了一個(gè)值,input輸入框會(huì)出現(xiàn)你輸入的值,但馬上input的值會(huì)被對(duì)應(yīng)的fiber的value屬性更新(finishEventHander 重置受控組件)。 如果沒(méi)有給input設(shè)置value則會(huì)忽略。
為什么合成事件屬性有時(shí)無(wú)法訪問(wèn)?
因?yàn)閞eact在事件流(捕獲到冒泡)完后就將合成事件對(duì)象釋放(SyntheticEvent.prototype.destructor 將合成事件對(duì)象的屬性重置)。
react如何模擬事件的阻止冒泡、阻止默認(rèn)行為?
react按照事件流順序執(zhí)行回調(diào),在執(zhí)行前會(huì)檢查當(dāng)前合成事件對(duì)象是否處于阻止冒泡的狀態(tài),如果是,則終止事件流。 react的合成事件對(duì)象原型對(duì)原生函數(shù)進(jìn)行增強(qiáng)。對(duì)原生事件方法的阻止冒泡、阻止默認(rèn)行為進(jìn)行了封裝(內(nèi)部也調(diào)用原生事件的方法)。
注釋
注1:根節(jié)點(diǎn),在 react-v16 為 document ,在 react-v17 為掛載容器Dom
注2:focus事件并不是冒泡事件,react對(duì)非冒泡事件在捕獲階段監(jiān)聽(tīng)
注3:根節(jié)點(diǎn)對(duì)所有事件進(jìn)行了監(jiān)聽(tīng),除了特別例外submit、reset、invalid和媒體事件等
注4:react將fiber樹(shù)掛載到Dom樹(shù)上時(shí),每個(gè)宿主(host)類(lèi)型fiber節(jié)點(diǎn)與Dom節(jié)點(diǎn)一一對(duì)應(yīng),并鏈接
注5:向上查找是因?yàn)榭赡茏覦om節(jié)點(diǎn)并不是被react管理的,如第三庫(kù)滾動(dòng)插件等
注6:為了模擬Dom事件,react進(jìn)行的補(bǔ)充
注7:react內(nèi)部一個(gè)構(gòu)造函數(shù)的實(shí)例,合成事件的部分屬性來(lái)源于nativeEvent。合成事件與fiber關(guān)聯(lián)
注8:收集而不是執(zhí)行。因?yàn)閞eact針對(duì)某一類(lèi)型事件的做了批處理
以上就是React合成事件詳解的詳細(xì)內(nèi)容,更多關(guān)于React合成事件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何將你的AngularJS1.x應(yīng)用遷移至React的方法
本篇文章主要介紹了如何將你的AngularJS1.x應(yīng)用遷移至React的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
react-beautiful-dnd 實(shí)現(xiàn)組件拖拽功能
這篇文章主要介紹了react-beautiful-dnd 實(shí)現(xiàn)組件拖拽功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
React hooks如何清除定時(shí)器并驗(yàn)證效果
在React中,通過(guò)自定義Hook useTimeHook實(shí)現(xiàn)定時(shí)器的啟動(dòng)與清除,在App組件中使用Clock組件展示當(dāng)前時(shí)間,利用useEffect鉤子在組件掛載時(shí)啟動(dòng)定時(shí)器,同時(shí)確保組件卸載時(shí)清除定時(shí)器,避免內(nèi)存泄露,這種方式簡(jiǎn)化了狀態(tài)管理和副作用的處理2024-10-10
解決React報(bào)錯(cuò)Property?'value'?does?not?exist?on?
這篇文章主要為大家介紹了React報(bào)錯(cuò)Property?'value'?does?not?exist?on?type?EventTarget的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
關(guān)于React Native報(bào)Cannot initialize a parameter of type''NSArra
這篇文章主要介紹了關(guān)于React Native報(bào)Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>錯(cuò)誤,本文給大家分享解決方案,需要的朋友可以參考下2021-05-05
react配置代理setupProxy.js無(wú)法訪問(wèn)v3.0版本問(wèn)題
這篇文章主要介紹了react配置代理setupProxy.js無(wú)法訪問(wèn)v3.0版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07

