日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

react hook——你可能不是“我”所认识的useEffect

發布時間:2025/7/25 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 react hook——你可能不是“我”所认识的useEffect 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

據說,這個hook可以模擬class組件的三個生命周期

前言

官網已經介紹過,這里再啰嗦一次。useEffect是一個用來執行副作用hook,第一個參數傳入一個函數,每一次render之后執行副作用和清除上一次副作用,該函數的返回值就是清除函數。第二個參數是一個數組,傳入內部的執行副作用函數需要的依賴,當這幾個依賴有一個要更新,effect里面也會重新生成一個新的副作用并執行副作用。如果沒有更新,則不會執行。如果第二個參數不傳,那么就是沒有說明自己有沒有依賴,那就是每次render該函數組件都執行。

很明顯,useEffect第一個參數可以模仿didmount、didupdate,它的返回值可以模仿willunmount

class組件生命周期模擬

"模仿生命周期,useEffect第二個參數傳個空數組,無依賴,只執行一次,相當于didmount。如果要區分生命周期,不傳第二個參數,每次都會跑,相當于didupdate。加個mount標記一下,里面用if判斷一下,即可以達到模擬生命周期的效果"

很多人都會想到這個辦法模擬,于是我們試一下看看:

let mount; function useForceUpdate() {const [_, forceUpdate] = useState(0);return () => forceUpdate(x => x + 1); }function UnmountTest() {useEffect(() => {if (!mount) {mount = true;console.log('did mount')} else {console.log('did update')}return () => {mount = false;console.log('unmount') }})const forceUpdate = useForceUpdate();return (<div>我是隨時被拋棄的<button onClick={forceUpdate}>強制更新</button></div>); }function State() {const [count, setCount] = useState(20);const handleCount = useCallback(() => {setCount(count => count + 1)}, [])return (<div>{count}<button onClick={handleCount}>count+1</button>{(count % 2) && <UnmountTest />}</div>) } 復制代碼

當count是奇數,那就展示UnmountTest,組件里面也有一個更新組件的方法。按照邏輯,useEffect不傳第二個參數,保證每次渲染都執行。然后加一個標記,標記第一次是掛載。于是運行一波看看

  • 點一下count+1,展示組件,打印didmount
  • 再點一下count,刪掉組件,打印unmount

符合預期,?

  • 點一下count+1,展示組件,打印didmount
  • 點一下強制更新,打印unmount、didmount,再點,還是一樣

??,什么鬼,居然不符合預期

useEffect是用來執行副作用,每一次render,將會清除上一次副作用、執行本次副作用(如果有依賴或者不傳入依賴數組)這個hook是以一個副作用為單位,當然也可以多次使用

這樣子說,每一次都是unmount、didmount,的確是符合這個邏輯,和"想當然"的那種模擬生命周期是有點不一樣的。這樣子,我們拆成兩個useEffect調用,就可以解決問題:

function UnmountTest() {useEffect(() => {if (mount) {console.log('did update')}});useEffect(() => {if (!mount) {console.log('did mount')mount = true;}return () => {console.log('unmount')mount = false;}}, []);const forceUpdate = useForceUpdate();return (<div>我是隨時被拋棄的<button onClick={forceUpdate}>強制更新</button></div>); } 復制代碼

這次,全都符合預期了,簡直ojbk?

useEffect & useLayoutEffect區別

useEffect是異步的,useLayoutEffect是同步的

我們看一下,一次組件從掛載到重新渲染,兩者的發生的時機:

從左到右表示時間線,紅色的是異步的,紅色框內是同步的,從上到下執行。useEffect是異步的,所謂的異步就是利用requestIdleCallback,在瀏覽器空閑時間執行傳入的callback。大部分情況下,用哪一個都是一樣的,如果副作用執行比較長,比如大量計算,如果是useLayoutEffect就會造成渲染阻塞。這只是一個case,我們可以看一下這個神奇的定時器:

點擊開始,開始計時,點擊暫停就暫停。點擊清0,暫停并且數字清零

function LYE() {const [lapse, setLapse] = React.useState(0)const [running, setRunning] = React.useState(false)useEffect(() => {if (running) {const startTime = Date.now() - lapseconst intervalId = setInterval(() => {setLapse(Date.now() - startTime)}, 2)console.log(intervalId)return () => clearInterval(intervalId)}},[running],)function handleRunClick() {setRunning(r => !r)}function handleClearClick() {setRunning(false)setLapse(0)}return (<div><label>{lapse}ms</label><button onClick={handleRunClick}>{running ? '暫停' : '開始'}</button><button onClick={handleClearClick}>暫停并清0</button></div>) } 復制代碼

于是,點擊清零居然不清0,只是停下來了,而且點開始也是繼續開始。這里只要把它改成useLayoutEffect就可以了,點清0馬上變成0并停止。另外,在使用useEffect下,把interval的時間改成大于16,有概率成功清0,如果更大一點是絕對清零。都說useEffect是異步,那么問題很有可能出現在異步這里。

useLayoutEffect是同步的,所以整個流程完全符合我們的預期,一切在掌控之中。基于兩點: useEffect里面的interval延遲太小并沒有清除計時結果、useEffect把interval延遲調到大于16后有概率解決。我們從這兩點出發,梳理一下useEffect執行時機:

這種情況是沒有清除定時器結果的,注意中間那塊:interval1 =》 render =》 clean useEffect1。 clean useEffect1之前又跑了一次interval1,interval1觸發render,展示的是當前計時結果。前面的stop操作, setRunning(false)和setLapse(0)的確是跑了,但是interval1又設置了當前計時結果,所以setLapse(0)就是白搞了。

把interval延遲調大

這種情況是正常的,顯然全部都在我們預期之內。經過多次測試,延遲臨界點是16ms。

為什么就是16ms?

有問題,很自然想到異步,說到異步又想到了requestIdleCallback,這個函數就是瀏覽器空閑的時候執行callback。類似于requestAnimationFrame,只是requestIdleCallback把優先級放低了。說到requestAnimationFrame就想到了平均60fps,接著1000/60 就是16.66666,所以每一幀的間隔大約是16ms左右。最后,問題來源就這樣暴露出來了,當interval間隔大于屏幕一幀時間,用useEffect此定時器不會有問題,反之則是interval會在useEffect之前多執行一次造成問題的出現。

如果文章對你有幫助,github , 掘金 可以關注一波哦

總結

以上是生活随笔為你收集整理的react hook——你可能不是“我”所认识的useEffect的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。