如何解決React useEffect鉤子帶來的無限循環問題

React的useEffect Hook可以讓用戶處理應用程序的副作用。例如:

  • 從網絡獲取數據:應用程序通常在第一次加載時獲取並填充數據。這可以通過useEffect函數實現
  • 操作UI:應用程序應該響應按鈕點擊事件(例如,打開一個菜單)
  • 設置或結束計時器:如果某個變量達到預定義值,則內置計時器應自行停止或啟動

盡管useEffect Hook在React生態系統中很常見,但它需要時間來掌握。因此,許多新手開發人員在配置他們的useEffect函數時,會導致無限循環問題。在本文中,您將瞭解不同場景下帶來的無限循環問題以及如何解決它們。

這是我們今天要學習的內容:

是什麼導致無限循環以及如何解決它們:

  • 在依賴項數組中不傳遞依賴項
  • 使用函數作為依賴項
  • 使用數組作為依賴項
  • 使用對象作為依賴項
  • 傳遞不正確的依賴項

什麼導致的無限循環以及如何解決它們

在依賴項數組中不傳遞依賴項

如果您的useEffect函數不包含任何依賴項,則會出現一個無限循環。

例如,看看下面的代碼:

function App() {
  const [count, setCount] = useState(0); //初始化值
  useEffect(() => {
    setCount((count) => count + 1); 
  }); //無依賴項
  return (
    <div className="App">
      <p> value of count: {count} </p>
    </div>
  );
}

如果沒有依賴關系,則默認在每個更新周期上觸發useEffect。因此,這裡的應用程序將在每次渲染時執行setCount函數。因此,這會導致一個無限循環:

gif在這裡插入圖片描述

是什麼導致瞭這個問題?讓我們一步一步來分析這個問題:

  • 在第一次渲染時,React會檢查count的值。在這裡,由於count為0,程序執行useEffect函數
  • 稍後,useEffect調用setCount方法並更新count的值
  • 之後,React重新呈現UI以顯示count的更新值
  • 此外,由於useEffect在每個呈現周期中運行,它將重新調用setCount函數
  • 由於上述步驟發生在每一個渲染,這導致你的應用程序崩潰

如何解決這個問題

為瞭緩解這個問題,我們必須使用依賴數組告訴React隻有在特定值更新時才調用useEffect。

下一步,像這樣附加一個空白數組作為依賴項:

useEffect(() => {
  setCount((count) => count + 1);
}, []); //empty array as second argument.

這告訴React在第一次裝載時執行setCount函數。

image

使用函數作為依賴項

如果你把一個方法傳入你的useEffect依賴數組,React會拋出一個錯誤,表明你有一個無限循環:

function App() {
  const [count, setCount] = useState(0);

  function logResult() {
    return 2 + 2;
  }
  useEffect(() => {
    setCount((count) => count + 1);
  }, [logResult]); // 函數作為依賴項
  return (
    <div className="App">
      <p> value of count: {count} </p>
    </div>
  );
}

在這段代碼中,我們將logResult方法傳遞給useEffect數組。理論上,React隻需要在第一次渲染時增加count的值。

image

是什麼導致瞭這個問題?

  • 要記住的一件事是,useEffect使用瞭一個叫做淺比較的概念。它這樣做是為瞭驗證依賴項是否已經更新
  • 這裡的問題是,在每次呈現期間,React都會重新定義logResult的引用
  • 因此,這將在每個循環中重新觸發useEffect函數
  • 因此,React會調用setCount鉤子,直到應用程序遇到更新深度錯誤。這會給程序帶來錯誤和不穩定性

如何解決這個問題

一個解決方案是使用useCallback鉤子。這允許開發人員記住他們的函數,從而確保引用值保持不變。由於這個參考值是穩定的,React不應該無限地重新渲染UI:

const logResult = useCallback(() => {
  return 2 + 2;
}, []); // logResult是緩存的
useEffect(()=> {
  setCount((count)=> count+1);
},[logResult]); //沒有無限循環錯誤,因為logResult引用保持不變。

結果:

image

使用數組作為依賴項

將數組變量傳遞給依賴項也會運行一個無限循環。考慮下面的代碼示例:

const [count, setCount] = useState(0); //初始值為0。
const myArray = ["one", "two", "three"];

useEffect(() => {
  setCount((count) => count + 1); // 和前面一樣,增加Count的值
}, [myArray]); // 將數組變量傳遞給依賴項

在這個塊中,我們將myArray變量傳入依賴參數。

gif

是什麼導致瞭這個問題?

既然myArray的值在整個程序中都沒有改變,為什麼我們的代碼會多次觸發useEffect ?

  • 在這裡,回想一下React使用淺比較來檢查依賴項的引用是否發生瞭變化。
  • 由於對myArray的引用在每次渲染時都在變化,useEffect將觸發setCount回調
  • 因此,由於myArray的引用值不穩定,React將在每個渲染周期中調用useEffect。最終,這會導致應用程序崩潰

如何解決這個問題

為瞭解決這個問題,我們可以使用useRefHook這將返回一個可變對象,確保引用不會改變:

const [count, setCount] = useState(0);
//提取“current”屬性並給它賦值
const { current: myArray } = useRef(["one", "two", "three"]);

useEffect(() => {
  setCount((count) => count + 1);
}, [myArray]); //依賴值是穩定的,所以沒有無限循環

將對象作為依賴項傳遞

在useEffect依賴數組中使用對象也會導致無限循環問題。

考慮下面的代碼:

const [count, setCount] = useState(0);
const person = { name: "Rue", age: 17 }; //創建一個對象
useEffect(() => {
  // 每次增加count的值
  // person的值發生瞭變化
  setCount((count) => count + 1);
}, [person]); // 依賴項數組包含一個對象作為參數
return (
  <div className="App">
    <p> Value of {count} </p>
  </div>
);

控制臺的結果表明程序是無限循環的:

img

是什麼導致瞭這個問題?

  • 和之前一樣,React使用淺比較來檢查person的參考值是否發生瞭變化
  • 因為person對象的引用值在每次渲染時都會改變,所以React會重新運行useEffect
  • 因此,在每個更新周期中調用setCount。這意味著我們現在有瞭一個無限循環

如何解決這個問題

那麼我們如何解決這個問題呢?

這就是usemmo的用武之地。**當依賴關系發生變化時,這個鉤子會計算一個記憶的值。**除此之外,因為我們記住瞭一個變量,這確保瞭狀態的引用值在每次渲染期間不會改變:

// 使用usemo創建一個對象
const person = useMemo(
  () => ({ name: "Rue", age: 17 }),
  [] //沒有依賴關系,所以值不會改變
);
useEffect(() => {
  setCount((count) => count + 1);
}, [person]);

傳遞不正確的依賴項

如果將錯誤的變量傳遞給useEffect函數,React將拋出一個錯誤。

下面是一個簡單的例子:

const [count, setCount] = useState(0);

useEffect(() => {
  setCount((count) => count + 1);
}, [count]); //註意,我們將count傳遞給瞭這個數組。

return (
  <div className="App">
    <button onClick={() => setCount((count) => count + 1)}>+</button>
    <p> Value of count{count} </p>
  </div>
);

gif

是什麼導致瞭這個問題?

  • 在上面的代碼中,我們告訴在useEffect方法中更新count的值
  • 此外,註意我們也將count Hook傳遞給瞭它的依賴數組
  • 這意味著每次count值更新時,React都會調用useEffect
  • 因此,useEffect鉤子調用setCount,從而再次更新count
  • 因此,React現在在一個無限循環中運行我們的函數

如何解決這個問題

要擺脫無限循環,隻需像這樣使用一個空的依賴數組:

const [count, setCount] = useState(0);
// 隻有在組件首次掛載時才更新'count'的值
useEffect(() => {
  setCount((count) => count + 1);
}, []);

這將告訴React在第一次渲染時運行useEffect。

gif

結尾

盡管React Hooks是一個簡單的概念,但是在將它們整合到項目中時,仍然需要記住許多規則。這將確保您的應用程序保持穩定,優化,並在生產過程中不拋出錯誤。

此外,最近發佈的Create React App CLI也會在運行時檢測和報告無限循環錯誤。這有助於開發人員在這些問題出現在生產服務器上之前發現並解決這些問題。

img

 到此這篇關於如何解決React useEffect鉤子帶來的無限循環問題的文章就介紹到這瞭,更多相關React useEffect鉤子無限循環內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: