【React】useEffectの無限ループを防ぐ方法:依存配列の正しい使い方

・はじめに

Reactの useEffect フックは、副作用(データの取得、購読、手動DOM操作など)を処理するために頻繁に使用されます。しかし、依存配列の設定を誤ると、意図せず無限ループが発生することがあります。本記事では、useEffect の無限ループを防ぐ方法と、依存配列の正しい使い方について詳しく解説します。

useEffect の基本

useEffect は、コンポーネントがレンダリングされた後に実行される関数です。基本的な構文は以下のとおりです。

useEffect(() => {
  // 副作用の処理
}, [依存配列]);

依存配列には、useEffect を再実行するトリガーとなる値を指定します。この依存配列の設定ミスが無限ループの原因になります。

・無限ループが発生するケース

2.依存配列を空にしない

以下のコードは、コンポーネントがリレンダリングされるたびに useEffect が実行され、無限ループに陥ります。

import { useState, useEffect } from "react";

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

  useEffect(() => {
    setCount(count + 1);
  }); // 依存配列なし

  return <div>Count: {count}</div>;
}

問題点

useEffect に依存配列が指定されていないため、コンポーネントがリレンダリングされるたびに実行され、setCountcount を変更。

count の変更が再レンダリングを引き起こし、useEffect が再び実行される。

解決策

useEffect の依存配列を適切に設定する。

useEffect(() => {
  setCount(count + 1);
}, []); // 空の依存配列を指定

しかし、この場合は count が更新されなくなるため、実際のユースケースに応じた対策が必要です。

2.関数を依存配列に入れる

次の例では、関数を依存配列に含めたため、無限ループが発生します。

function App() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    increment();
  }, [increment]);

  return <div>Count: {count}</div>;
}

問題点

increment 関数は useEffect 内で毎回新しく作られるため、依存配列が変化し、無限ループに。

解決策

useCallback を使用して increment 関数の参照を維持する。

import { useCallback } from "react";

const increment = useCallback(() => {
  setCount(count + 1);
}, [count]);

3.オブジェクトや配列を依存配列に含める

以下のコードも無限ループになります。

function App() {
  const [data, setData] = useState({ value: 0 });

  useEffect(() => {
    console.log("Effect runs");
  }, [data]);

  return <div>Data: {data.value}</div>;
}

問題点

・オブジェクト data はリレンダリングのたびに新しい参照を持つため、useEffect が常に実行される。

解決策

・useState の setter 関数内で更新する。

useEffect(() => {
  setData(prev => ({ ...prev, value: prev.value + 1 }));
}, []); // 依存配列を空にするか、特定の値だけを監視する

または useMemo を利用して data の参照を固定する。

const memoizedData = useMemo(() => ({ value: data.value }), [data.value]);

・useEffect の依存配列の正しい使い方

●基本ルール

1.依存配列を慎重に設定する

本当に監視する必要のある値だけを入れる。

2.関数は useCallback でメモ化する

useCallback を使用しないと関数が毎回新しい参照になるため、無限ループを引き起こす。

3.オブジェクトや配列は useMemo でメモ化する

useMemo を使うことで、不要な再計算を防ぐ。

4.非同期処理(API呼び出しなど)は useEffect 内で適切に扱う

useEffect 内で setState するときは、依存配列の管理を慎重に行う。

useEffect(() => {
  fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => setData(data));
}, []); // 必要な場合のみ実行する

・まとめ

useEffect の無限ループは、依存配列の設定ミスで発生しやすい。
適切な依存配列の管理関数のメモ化(useCallback)オブジェクトのメモ化(useMemo) を意識する。
・不要な useEffect の実行を避けるために、ステートの変更方法を工夫する。

これらのポイントを押さえることで、より効率的でバグの少ない React アプリを構築できます!

タイトルとURLをコピーしました