新聞中心
大家好,我是前端西瓜哥。

公司主營(yíng)業(yè)務(wù):網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出蒼溪免費(fèi)做網(wǎng)站回饋大家。
最近公司的項(xiàng)目用的 React 從 16 升到了 17 版本,選擇升級(jí)的原因是想以后將項(xiàng)目遷移到 Nextjs 上。
結(jié)果發(fā)現(xiàn)因?yàn)?React 的行為不一致導(dǎo)致了一些看得見(jiàn)的和看不見(jiàn)的 bug,真的是一場(chǎng)災(zāi)難。
React 17 是一個(gè)比較特別的版本,它沒(méi)有任何新特性,但它改造了 React 的底層,讓 React 17 可以漸進(jìn)式地升級(jí)部分的模塊,為 18 版本做準(zhǔn)備。
這些改造,有一部分是破壞性的。
問(wèn)題
我這里有個(gè)彈窗,彈窗里面是一些表單項(xiàng)。當(dāng)用戶在表單項(xiàng)做了一些修改,然后點(diǎn)擊彈窗的遮罩層時(shí),里面的組件會(huì)銷毀掉。
在銷毀時(shí),我們會(huì)調(diào)用一個(gè) ref 上的 save() 方法來(lái)保存這些數(shù)據(jù)。
useEffect(() => {
return () => {
formRef.current && formRef.current.save();
}
}, []);
在 React 16 的時(shí)候是正常的,但到 React 17,失敗了,我們無(wú)法保存表單里面的數(shù)據(jù)。
一頓排查之后,我找到了問(wèn)題所在:在 React 17 版本,組件銷毀時(shí)獲取的 ref.current 可能會(huì)被重置為 null。
接著我找到了官方文檔對(duì)于這種情況的說(shuō)明:
https://zh-hans.reactjs.org/blog/2020/08/10/react-v17-rc.html#effect-cleanup-timing
useEffect 的清理時(shí)機(jī)
useEffect(() => {
return () => {
// 這里的執(zhí)行清理操作
}
})
在 React 17 中,副作用的執(zhí)行時(shí)機(jī)發(fā)生了變化,一個(gè)破壞性的效果是:如果組件卸載,副作用的清理時(shí)機(jī)是異步的,對(duì)應(yīng)的回調(diào)函數(shù)執(zhí)行也同樣是異步的。是的,異步。
卸載時(shí)的要執(zhí)行的回調(diào)函數(shù),對(duì)于狀態(tài)和方法的訪問(wèn),問(wèn)題不大,它們是不可變的,能通過(guò)閉包的方式訪問(wèn)到的。
但問(wèn)題是 ref,它是可變的,我們可以隨意的設(shè)置 ref.current 的值,且不會(huì)觸發(fā)組件的重新渲染。
這個(gè) ref 會(huì)被 React 在組件卸載時(shí)重置為 null。因?yàn)槭钱惒降?,所以我們有很大可能?huì)喜提一個(gè) null 值。
這里有一個(gè)簡(jiǎn)單的在線 demo,感興趣可以看看,當(dāng) Component 組件銷毀時(shí),elRef 變成了 null:
https://codesandbox.io/s/react-17-zhong-zu-jian-xiao-hui-shi-ref-ke-neng-bei-she-zhi-wei-null-2kl4xu。
然后是 React 16 版本的 ref,因?yàn)槭峭降?,所以銷毀時(shí) ref 沒(méi)有重置為 null:
https://codesandbox.io/s/16-de-ref-shi-zheng-chang-de-18mqmd。
解決方案
官方的文檔提供了兩個(gè)解決方案。
一個(gè)是用 useLayoutEffect。
useLayoutEffect(() => {
return () => {
formRef.current && formRef.current.save();
}
}, []);
useLayoutEffect 可以保證回調(diào)函數(shù) 同步 執(zhí)行,這樣就能確保 ref 此時(shí)還是最后的值,而不是被設(shè)置為 null。
第二種方式是用一個(gè)臨時(shí)變量在 ref 每次變化時(shí),將 ref.current 保存起來(lái),放到副作用清理回調(diào)函數(shù)的閉包中,來(lái)保證不可變性。
useEffect(() => {
const instance = someRef.current;
instance.someSetupMethod();
return () => {
instance.someCleanupMethod();
};
});
但這里貌似還是有一點(diǎn)局限性:不能提供第二個(gè)參數(shù),也就是依賴項(xiàng)參數(shù),因?yàn)槲覀儾荒鼙WC中途 ref 沒(méi)有發(fā)生改變。
目前我是用第一種方案來(lái)處理我遇到的問(wèn)題。
結(jié)尾
版本升級(jí)這件事情,還是得權(quán)衡利弊。
標(biāo)題名稱:React16升級(jí)到17的一個(gè)坑:組件銷毀時(shí)Ref可能會(huì)被重置為Null
文章出自:http://fisionsoft.com.cn/article/dpgpogo.html
其他資訊
- Java比較不同數(shù)據(jù)庫(kù)的數(shù)據(jù):優(yōu)劣與差異(java對(duì)比不同數(shù)據(jù)庫(kù)數(shù)據(jù))
- 4G網(wǎng)絡(luò)驅(qū)動(dòng)Linux新時(shí)代開(kāi)啟(4g驅(qū)動(dòng)linux)
- PHP一鍵批量導(dǎo)入數(shù)據(jù)庫(kù)(php批量加入數(shù)據(jù)庫(kù)中)
- 排查Redis超時(shí)原因的過(guò)程(redis超時(shí)怎么排查)
- windows怎么連接ftp服務(wù)器?(如何windows連接ftp服務(wù)器配置)


咨詢
建站咨詢
