這篇文章主要介紹“react中useEffect閉包問題怎么解決”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“react中useEffect閉包問題怎么解決”文章能幫助大家解決問題。
公司主營業(yè)務(wù):網(wǎng)站制作、成都網(wǎng)站設(shè)計、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出鑲黃免費做網(wǎng)站回饋大家。
看一段因為useEffect導(dǎo)致的閉包問題代碼
const btn = useRef(); const [v, setV] = useState(''); useEffect(() => { let clickHandle = () => { console.log('v:', v); } btn.current.addEventListener('click', clickHandle) return () => { btn.removeEventListener('click', clickHandle) } }, []); const inputHandle = e => { setV(e.target.value) } return ( <> > )
useEffect的依賴項數(shù)組為空,所以在頁面渲染完成之后,內(nèi)部代碼只會執(zhí)行一次,頁面銷毀再執(zhí)行一次。此時在輸入框中輸入任意字符,再點擊測試按鈕,得到的輸出為空,之后無論如何輸入任何字符,再點擊測試按鈕時,輸出的結(jié)果仍為空。
為什么會這樣呢?其實就是閉包所造成的。
函數(shù)的作用域在函數(shù)定義的時候就決定了
給btn注冊點擊事件時,作用域如下:
能訪問到的自由變量v此時還是空值。當(dāng)點擊事件觸發(fā)時,執(zhí)行點擊回調(diào)函數(shù),此時先創(chuàng)建執(zhí)行上下文,會拷貝作用域鏈到執(zhí)行上下文中。
如果未在輸入框內(nèi)輸入字符,此時點擊拿到的v還是原來那個v
如果在輸入框內(nèi)輸入了字符,此時調(diào)用了setV修改了state,頁面觸發(fā)render,組件內(nèi)部代碼會重新執(zhí)行一遍,重新聲明了一個v,v就不再是原來那個v,這里點擊事件里作用域中的v還是舊的v,這是兩個不同的v
事件綁定。比如示例代碼中,在頁面最初渲染完成后只綁定一次事件的情況,比如使用echarts,在useEffect中獲取echarts的實例并綁定事件
定時器。頁面加載后注冊一個定時器,定時器內(nèi)的函數(shù)也會產(chǎn)生如此的閉包問題。
針對這個閉包問題下面大致給出5種解決辦法
1. 以賦值方式直接修改v,并將修改v的方法用useCallback包裹起來
將修改v的方法用useCallback包裹起來,被useCallback包裹的函數(shù)將被緩存,由于依賴項的數(shù)組為空,所以這里直接賦值的方式修改的v是舊的v,此種方法不推薦,因為setState才是官方推薦的修改state的方式,這里仍然使用setV只是為了觸發(fā)rerender
// v 的聲明 由 const 改為 var,方便直接修改 var [v, setV] = useState(''); const inputHandle = useCallback(e => { let { value } = e.target v = value setV(value) }, [])
2. 給useEffect的依賴項加上v
這也許是大多數(shù)人首先想到的辦法,既然v是舊的,那么每次v更新的時候,重新注冊一次事件不就行了,但是這樣的會導(dǎo)致每次v更新都得重新注冊,理論應(yīng)該只需要注冊一次的事件變成了多次。
3. 避免v被重新聲明
以let或var的方式聲明某個變量代替v,直接修改這個變量,而不是要setState相關(guān)函數(shù)觸發(fā)render,這樣就不會被重新聲明,點擊的回調(diào)函數(shù)里就能拿到“最新”的值,但這個方法更不推薦,就此示例來說,input組件由于沒有rerender而至始至終都是顯示空值,不符合操作預(yù)期。
4. 使用useRef代替useState
const btn = useRef(); const vRef = useRef(''); const [v, setV] = useStat(''); useEffect(() => { let clickHandle = () => { console.log('v:', vRef.current); } btn.current.addEventListener('click', clickHandle) return () => { btn.removeEventListener('click', clickHandle) } }, []); const inputHandle = e => { let { value } = e.target vRef.current = value setV(value) } return ( <> > )
useRef的方案之所以有效,是因為每次input的change修改的是vRef這個對象的current屬性,而vRef始終是那個vRef,即使rerender,由于vRef是對象,所以變量存儲在棧內(nèi)存中的值是該對象在堆內(nèi)存中的地址,只是一個引用,只修改對象的某個屬性,該引用并不會改變。所以點擊事件中的作用域鏈始終訪問的都是同一個vRef
5. 將v換成對象類型
其實和使用useRef一樣,只要是對象,僅修改某個屬性也不會改變該state所指向的地址。
關(guān)于“react中useEffect閉包問題怎么解決”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。