React報錯之Too many re-renders

2022-12-14 06:00:48

總覽

產生"Too many re-renders. React limits the number of renders to prevent an infinite loop"錯誤有多方面的原因:

  1. 在一個元件的渲染方法中呼叫一個設定狀態的函數。
  2. 立即呼叫一個事件處理器,而不是傳遞一個函數。
  3. 有一個無限設定與重渲染的useEffect勾點。

這裡有個範例來展示錯誤是如何發生的:

import {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  // ⛔️ Too many re-renders. React limits the number
  // of renders to prevent an infinite loop.
  return (
    <div>
      <button onClick={setCounter(counter + 1)}>Increment</button>
      <h1>Count: {counter}</h1>
    </div>
  );
}

上述程式碼問題在於,我們在onClick事件處理器中立即呼叫了setCounter函數。

該函數是在頁面載入時立即被呼叫,而不是事件觸發後呼叫。

傳遞函數

為了解決該錯誤,為onClick事件處理器傳遞函數,而不是傳遞呼叫函數的結果。

import {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <button onClick={() => setCounter(counter + 1)}>Increment</button>
      <h1>Count: {counter}</h1>
    </div>
  );
}

現在,我們為事件處理器傳遞了函數,而不是當頁面載入時呼叫setCounter方法。

如果該方法在頁面載入時被呼叫,就會觸發一個setState動作,元件就會無限重新渲染。

如果我們試圖立即設定一個元件的狀態,而不使用一個條件或事件處理器,也會發生這個錯誤。

import {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  // ⛔️ Too many re-renders. React limits the number
  // of renders to prevent an infinite loop.
  setCounter(counter + 1);

  return (
    <div>
      <h1>Count: {counter}</h1>
    </div>
  );
}

問題在於,setCounter函數在元件渲染時被呼叫、更新狀態,並導致重新渲染,而且是無限重新渲染。

你可以通過向useState()勾點傳遞一個初始值或一個函數來初始化狀態,從而解決這個錯誤。

import {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(() => 100 + 100);

  return (
    <div>
      <h1>Count: {counter}</h1>
    </div>
  );
}

我們向useState方法傳遞了一個函數。這個函數只會在元件第一次渲染時被呼叫,並且會計算出初始狀態。你也可以直接向useState方法傳遞一個初始值。

另外,你也可以像前面的例子那樣使用一個條件或事件處理器。

import {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  //