一文弄懂React的useEffect經(jīng)常執(zhí)行兩次
引言
在使用React開發(fā)應(yīng)用時,許多開發(fā)者都會遇到一個看似奇怪的現(xiàn)象:useEffect鉤子在某些情況下會執(zhí)行兩次。這種現(xiàn)象在開發(fā)模式下尤其常見,而在生產(chǎn)模式下則通常不會出現(xiàn)。對于初次接觸React或?qū)eact內(nèi)部機(jī)制理解不深的開發(fā)者來說,這可能會引發(fā)困惑甚至導(dǎo)致錯誤的調(diào)試方向。本文將深入探討這一現(xiàn)象的根本原因,分析React的設(shè)計理念,并解釋為什么這種行為實(shí)際上是React團(tuán)隊有意為之的優(yōu)化策略。
主體
1. useEffect的基本行為
首先,我們需要明確useEffect的基本工作原理。useEffect是React Hooks中用于處理副作用的核心API,它的執(zhí)行時機(jī)是在組件渲染完成之后。根據(jù)依賴項的不同,useEffect的執(zhí)行可以分為以下幾種情況:
- 無依賴數(shù)組:每次渲染后都會執(zhí)行
- 空依賴數(shù)組:僅組件掛載時執(zhí)行一次
- 有依賴項:依賴項變化時執(zhí)行
然而,即便是在空依賴數(shù)組的情況下,開發(fā)者有時也會觀察到useEffect被執(zhí)行兩次,這與預(yù)期不符。這種現(xiàn)象主要發(fā)生在React 18及更高版本的開發(fā)模式下。
2. React 18的嚴(yán)格模式(Strict Mode)
React 18引入了一個重要的變化:在開發(fā)模式下默認(rèn)啟用了嚴(yán)格模式(Strict Mode)。嚴(yán)格模式會故意讓你的組件掛載兩次,以此來幫助發(fā)現(xiàn)潛在的問題。這種行為的目的是:
- 檢測不純的渲染(如直接修改props或state)
- 驗證effect的清理函數(shù)是否正確實(shí)現(xiàn)
- 發(fā)現(xiàn)意外的副作用
當(dāng)組件掛載兩次時,相關(guān)的useEffect也會隨之執(zhí)行兩次。這是React團(tuán)隊故意設(shè)計的,目的是幫助開發(fā)者在早期發(fā)現(xiàn)并修復(fù)問題,而不是等到生產(chǎn)環(huán)境中才暴露出來。
3. 雙重渲染的深層機(jī)制
React 18的雙重渲染機(jī)制實(shí)際上是一種"mount → unmount → remount"的過程。具體來說:
- 首次渲染和掛載組件
- 執(zhí)行所有effect(第一次執(zhí)行)
- React立即卸載組件
- 執(zhí)行所有effect的cleanup函數(shù)
- 重新掛載組件(第二次執(zhí)行effect)
這種機(jī)制確保了你的組件能夠正確處理"掛載→卸載→重新掛載"的生命周期,這在現(xiàn)實(shí)應(yīng)用中是很常見的場景(如用戶在頁面間快速導(dǎo)航)。
4. 為什么只在開發(fā)模式下?
這種雙重渲染行為僅限于開發(fā)模式,原因如下:
- 性能考量:雙重渲染會增加額外的計算開銷
- 調(diào)試目的:生產(chǎn)環(huán)境不需要這種嚴(yán)格的檢查
- 用戶體驗:不應(yīng)該影響最終用戶的體驗
React團(tuán)隊通過這種方式在開發(fā)階段模擬了更嚴(yán)格的環(huán)境,幫助開發(fā)者提前發(fā)現(xiàn)潛在問題。
5. 實(shí)際案例分析
讓我們通過幾個具體例子來說明這種現(xiàn)象:
案例1:空依賴數(shù)組
useEffect(() => {
console.log("Effect ran");
return () => console.log("Cleanup ran");
}, []);
在開發(fā)模式下,你會看到以下輸出:
Effect ran Cleanup ran Effect ran
案例2:API調(diào)用
useEffect(() => {
fetchData();
}, []);
這種代碼在開發(fā)模式下會觸發(fā)兩次API調(diào)用,可能導(dǎo)致意外的副作用。正確的做法應(yīng)該是添加取消邏輯:
useEffect(() => {
const controller = new AbortController();
fetchData({ signal: controller.signal });
return () => controller.abort();
}, []);
6. 如何正確處理雙重執(zhí)行
為了避免雙重執(zhí)行帶來的問題,開發(fā)者應(yīng)該:
- 確保effect有正確的清理函數(shù)
- 對于冪等操作(如日志記錄)可以忽略重復(fù)執(zhí)行
- 對于非冪等操作(如POST請求)應(yīng)該添加防護(hù)
- 使用refs來跟蹤effect是否已經(jīng)執(zhí)行過
7. 設(shè)計哲學(xué)探討
React團(tuán)隊選擇這種設(shè)計有幾個深層次的考慮:
- 可恢復(fù)性:確保組件能在任何時刻被正確地重新掛載
- 一致性:開發(fā)環(huán)境盡可能模擬生產(chǎn)環(huán)境的邊緣情況
- 可預(yù)測性:通過嚴(yán)格模式暴露潛在問題,而不是隱藏它們
這種設(shè)計鼓勵開發(fā)者編寫更健壯的代碼,能夠處理各種邊界情況。
8. 性能優(yōu)化技巧
雖然雙重渲染在開發(fā)模式下是有意的行為,但我們?nèi)钥梢酝ㄟ^一些技巧來優(yōu)化性能:
- 使用
useMemo和useCallback減少不必要的重新計算 - 將大型effect拆分為多個小型effect
- 合理設(shè)置依賴數(shù)組,避免過度觸發(fā)
- 考慮使用
useLayoutEffect處理必須同步執(zhí)行的副作用
9. 常見誤解澄清
關(guān)于useEffect的雙重執(zhí)行,有幾個常見的誤解需要澄清:
- 這不是bug:這是React團(tuán)隊有意為之的設(shè)計
- 不影響生產(chǎn)環(huán)境:生產(chǎn)構(gòu)建會自動禁用這種行為
- 不是所有effect都會執(zhí)行兩次:只有某些特定情況下才會出現(xiàn)
- 不是React 18特有的:早期版本在某些配置下也有類似行為
10. 與其他框架的對比
對比其他主流框架的處理方式:
- Vue:在開發(fā)模式下也有類似的警告機(jī)制,但不會強(qiáng)制雙重執(zhí)行
- Angular:依賴變更檢測策略,開發(fā)模式下的行為差異較小
- Svelte:編譯時優(yōu)化,較少出現(xiàn)運(yùn)行時意外行為
React的選擇體現(xiàn)了其"顯式優(yōu)于隱式"的設(shè)計哲學(xué)。
總結(jié)
useEffect在開發(fā)模式下的雙重執(zhí)行行為是React團(tuán)隊精心設(shè)計的功能,旨在幫助開發(fā)者提前發(fā)現(xiàn)并解決潛在的問題。理解這一行為背后的設(shè)計理念和實(shí)現(xiàn)機(jī)制,有助于我們編寫更健壯、更可靠的React組件。作為開發(fā)者,我們應(yīng)該:
- 接受并理解這種設(shè)計選擇
- 確保effect邏輯具備冪等性或正確的清理機(jī)制
- 利用嚴(yán)格模式提高代碼質(zhì)量
- 區(qū)分開發(fā)和生產(chǎn)環(huán)境的行為差異
通過這種方式,我們可以充分利用React提供的開發(fā)工具,構(gòu)建出更高質(zhì)量的應(yīng)用程序。記住,React的這些"看似奇怪"的行為背后,往往有著深思熟慮的設(shè)計考量。
到此這篇關(guān)于一文弄懂React的useEffect經(jīng)常執(zhí)行兩次的文章就介紹到這了,更多相關(guān)React useEffect執(zhí)行兩次內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Cloud?Studio構(gòu)建React完成點(diǎn)餐H5頁面(騰訊云?Cloud?Studio?實(shí)戰(zhàn)訓(xùn)練營)
最近也是有機(jī)會參與到了騰訊云舉辦的騰訊云Cloud Studio實(shí)戰(zhàn)訓(xùn)練營,借此了解了騰訊云Cloud?Studio產(chǎn)品,下面就來使用騰訊云Cloud?Studio做一個實(shí)戰(zhàn)案例來深入了解該產(chǎn)品的優(yōu)越性吧,感興趣的朋友跟隨小編一起看看吧2023-08-08
詳解react-native WebView 返回處理(非回調(diào)方法可解決)
這篇文章主要介紹了詳解react-native WebView 返回處理(非回調(diào)方法可解決),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02
使用react-color實(shí)現(xiàn)前端取色器的方法
本文通過代碼給大家介紹了使用react-color實(shí)現(xiàn)前端取色器的方法,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-11-11
React虛擬渲染實(shí)現(xiàn)50個或者一百個圖表渲染
這篇文章主要為大家介紹了React虛擬渲染實(shí)現(xiàn)50個或者100個圖表渲染的實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
React通過hook實(shí)現(xiàn)封裝表格常用功能
這篇文章主要為大家詳細(xì)介紹了React通過hook封裝表格常用功能的使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考下2023-12-12
React?Hooks之useDeferredValue鉤子用法示例詳解
useDeferredValue鉤子的主要目的是在React的并發(fā)模式中提供更流暢的用戶體驗,特別是在有高優(yōu)先級和低優(yōu)先級更新的情況下,本文主要講解一些常見的使用場景及其示例2023-09-09

