以前、業務の中でStrict Mode環境においてuseEffectが二重に発火することが原因で不具合が発生しました。useEffectの中に書いていた処理が意図せず2回実行され、想定外の状態更新が起きていたのです。
この不具合を解消する過程で、自分がuseEffectを使うべきタイミングやStrict Modeとの付き合い方を理解できていなかったことに気づき、あらためてReact公式サイトを読み直しました。
本記事では、React公式の内容をもとに、useEffectを使うべきタイミングと、依存配列・Strict Modeとの付き合い方を整理します。
1. useEffectの役割
React公式では、useEffectはReactのコードの外にあるものとコンポーネントを同期するためのものと説明されています。
例えば、ブラウザAPI、DOM操作、イベント購読、ネットワーク接続、外部ライブラリなどがこれに当たります。
つまり、useEffectを使うかどうかの判断軸は、
「その処理は、外部システムとの同期か?」
で考えるのがシンプルです。
2. useEffectを使う場面
2-1. イベント購読の登録・解除
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
これは window という外部の仕組みと同期しています。
コンポーネントが表示されている間だけイベントを購読し、不要になったら解除する、という用途なのでuseEffectが適しています。
2-2. 外部ライブラリやDOMとの連携
タイマー、アニメーションライブラリのインスタンス管理などもuseEffectの出番です。
Reactのレンダリングだけで完結しない処理は、useEffectで扱うのが自然です。
2-3. 画面表示に応じたデータ取得
データ取得は、外部のネットワークとのやり取りであるため、Effectを使う代表例です。
useEffect(() => {
let ignore = false;
async function fetchData() {
const response = await fetch("/api/users");
const result = await response.json();
if (!ignore) {
setUsers(result);
}
}
fetchData();
return () => {
ignore = true;
};
}, []);
ignore フラグを使ったcleanupは、コンポーネントのアンマウント後にstateを更新してしまうのを防ぐためのパターンです。
ただし、React公式ではuseEffectでの直接的なfetchについて、フレームワーク組み込みのデータ取得機能やReact Query(TanStack Query)などのライブラリの利用も検討すべきとしています。競合状態の処理、キャッシュ、重複リクエストの抑制などを自前で実装する必要があり、実務では意外と考慮点が多いためです。
3. 依存配列の扱い
useEffectはデフォルトで毎回のレンダリング後に実行されます。そのため、多くの場合は依存配列を指定して「どの値が変わったときに再同期するか」を明示する必要があります。
依存配列を手動で削ったり、意図的にごまかそうとすると、かえってバグの原因になります。
React公式のESLintルール(exhaustive-deps)でも、Effectは依存している値に合わせて再同期されるべきだとされています。
依存配列についてのポイント:
-
[](空配列):マウント時に1回だけ実行し、アンマウント時にcleanupが走る -
[value]:valueが変わるたびに再実行される - 依存配列なし:毎回のレンダリング後に実行される(通常は意図しない挙動になりがち)
4. Strict Modeでの二重実行
開発環境でStrict Modeが有効な場合、初回マウント時に setup → cleanup → setup の追加サイクルが走ります。
これは不具合ではなく、cleanupが正しく書けているかを確認するための意図的な挙動です。
つまり、Strict Modeで問題が起きるということは、そのuseEffectのcleanup処理に不備がある、あるいはそもそもuseEffectを使うべきでない処理を書いている、というサインです。
自分の場合は、二重に発火しても問題ないようにcleanupを見直すか、そもそもEffectでやるべき処理ではなかったのかを切り分けることで、不具合を解消できました。
5. まとめ
useEffectを使うかどうかの判断は、「外部システムとの同期が必要か?」というシンプルな問いに集約できます。
また、依存配列を正しく指定すること、Strict Modeでの二重実行に耐えられるcleanupを書くことが、useEffectを安定して使うためのポイントです。
次の記事では、useEffectを使わなくてよい場面についてReact公式を読んで整理します。propsやstateからの計算、イベントハンドラで完結する処理など、Effectに頼らない方がシンプルに書けるケースをまとめます。