我們前面花了大量篇幅,從基礎(chǔ)、理論、實踐、總結(jié)幾個方面,全方位的為大家分析了 useEffect。除此之外,React 還提供了一個與 useEffect 幾乎一樣的 hook,它就是useLayoutEffect。
我們約定,useEffect 傳入的第一個參數(shù)為 effect,useLayoutEffect 傳入的第一個參數(shù)為 layoutEffect。
他們的語法為:
// 中括號表示參數(shù)可選useEffect(effect[, deps])
useLayoutEffect(layoutEffect[, deps])
兩個 hook 有高度相似的語義。
第一個參數(shù) layoutEffect 為一個函數(shù),定義為副作用執(zhí)行邏輯,我們也可以在 layoutEffect 中定義返回函數(shù)。當(dāng)依賴項發(fā)生了變化時,返回函數(shù)會使用依賴項舊值首先執(zhí)行,然后再執(zhí)行 layoutEffect。
useLayoutEffect(() => { // ... return () => {}}, [state])
第二個參數(shù)為依賴項數(shù)組。React 內(nèi)部會使用 Object.is 去比較依賴項是否發(fā)生了變化,我們通常會選擇使用 state 或者 props 等響應(yīng)性數(shù)據(jù)作為依賴項。依賴項也可以不傳,此時 layoutEffect 在每次狀態(tài)發(fā)生變化時都會執(zhí)行。
useLayoutEffect 與 useEffect 唯一的區(qū)別在于 effect 與 layoutEffect 執(zhí)行時機(jī)的不同。
我們借助一個例子來仔細(xì)分析他們的準(zhǔn)確執(zhí)行時機(jī)。
首先是 useEffect。
const [count, setCount] = useState(0)useEffect(() => { document.title = `React ${count}`})
effect 會在組件渲染完成之后執(zhí)行。這里組件渲染完成的意思是當(dāng)組件內(nèi)容已經(jīng)呈現(xiàn)在頁面上之后,effect 再執(zhí)行,具體的步驟如下圖所示:
在事件循環(huán)中, effect 是被定義為宏任務(wù),在下一輪循環(huán)執(zhí)行。
然后是 useLayoutEffect。
const [count, setCount] = useState(0)useLayoutEffect(() => { document.title = `React ${count}`})
layoutEffect 會在組件渲染之前執(zhí)行。具體的步驟如下圖。
但是這里如果只是這樣理解的話,估計很多人并不太清晰具體是怎么回事。因為這樣的表達(dá)并沒有說清楚具體的執(zhí)行時刻。更準(zhǔn)確的說法是在 commit 之后,組件內(nèi)容繪制呈現(xiàn)到屏幕之前。
例如我們有這樣一段代碼。
// 此時已經(jīng)對DOM發(fā)送改變的指令div.style.color = 'red'layoutEffect()
layoutEffect 緊隨 DOM 修改指令發(fā)出之后執(zhí)行,此時雖然 DOM 指令已經(jīng)發(fā)出,但是在瀏覽器的機(jī)制中,內(nèi)容繪制是一個異步的過程,這會兒繪制并沒有執(zhí)行。
因此在事件循環(huán)中,layoutEfect 被定義為類似于 Promise 的微任務(wù),在 DOM 指令修改之后,內(nèi)容繪制之前執(zhí)行。
大家可以猜想一下,如果我們在 layoutEffect 中直接去修改 state,會發(fā)生什么事情。
看看下面這個例子:
function Demo() { const [count, setCount] = useState(0) useLayoutEffect(() => { if (count == 0) { setCount(1) } }, [count]) return ( <div> <div>{count}</div> <button onClick={() => setCount(0)} > reset 0 </button> </div> )}
我們在 state 中聲明一個變量 count,初始值設(shè)置為 0,并定義 layoutEffect,其中的邏輯就是當(dāng) count == 0 時,將 count 設(shè)置為 1。
添加一個按鈕,當(dāng)按鈕點擊時,把 count 重新設(shè)置為 0。
大家思考一下,此時,頁面上的顯示結(jié)果,會在 0 和 1 之間來回切換嗎?
答案是不會。
因為當(dāng)我們執(zhí)行 layoutEffect 時,UI 并沒有進(jìn)入事件循環(huán)的繪制流程,此時還處于 JS 邏輯的執(zhí)行過程中,那么這個時候執(zhí)行 setCount,整個邏輯會重新執(zhí)行,對于瀏覽器而言,JS 針對同一個 UI 發(fā)出了兩條不同的指令,在瀏覽器的渲染機(jī)制中,也會發(fā)生收集行為,將這兩條指令進(jìn)行合并,最后只執(zhí)行一條。
// setCount(0)div.innerHTML = 0// setCount(1)div.innerHTML = 1
如上例,當(dāng) setCount(0) 與 setCount(1) 執(zhí)行完之后,實際上是發(fā)出了兩條修改元素內(nèi)容的指令給到瀏覽器。
當(dāng)我們使用 useLayoutEffect 時他可能會覆蓋你想要執(zhí)行的渲染內(nèi)容,也有可能會阻塞你的正常渲染過程,因此我們在使用它時,需要精確把控他的執(zhí)行時機(jī),防止出現(xiàn)你不想看到的結(jié)果。
但是很明顯我們可以看到 layoutEffect 的執(zhí)行時機(jī)比 effect 更早。因此我們也可以在 layoutEffect 中,執(zhí)行一些輕量的,不直接影響 state 的邏輯。
本文鏈接:http://www.www897cc.com/showinfo-26-63227-0.html精準(zhǔn)解析 useLayoutEffect 與 useEffect 的執(zhí)行時機(jī)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Java高頻面試題:過濾器和攔截器兩位難兄難弟區(qū)別
下一篇: 這篇文章徹底讓你了解Java與RPA