・はじめに
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
に依存配列が指定されていないため、コンポーネントがリレンダリングされるたびに実行され、setCount
が count
を変更。
・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 アプリを構築できます!