React 18 StrictMode で useEffect が2回実行される理由と対処法

React 18 以降、StrictMode を有効にしていると useEffect が「2回実行される」という挙動に驚いた開発者は少なくありません。特に、API へのリクエストやログ出力のような副作用が重複して起きると「バグではないか?」と疑いたくなるでしょう。しかしこれは仕様であり、React チームが意図的に導入したものです。

本記事では、その背景と正しい理解、そして実務上どのように対応すべきかを解説します。

1. React 18 StrictMode の仕様変更

React 18 から StrictMode の下では「副作用が意図通りにクリーンアップされているか」を検出するために、マウントとアンマウントのシミュレーションが追加されました。

この結果、useEffect が「初回マウント → クリーンアップ → 再マウント」という流れをたどり、見かけ上 2 回実行されるように見えます。これは開発環境でのみ起こる挙動であり、本番ビルドでは通常通り 1 回しか実行されません。目的は副作用のリークを早期に発見することにあり、バグではなく「安全性を高めるための仕様」と理解すべきです。

2. 実際の再現例

以下のコードは典型的な例です。useEffect 内でログを出力してみましょう。

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    console.log('Effect executed');
    return () => console.log('Cleanup executed');
  }, []);

  return <div>Hello React 18</div>;
}

export default Example;

これを StrictMode 環境でレンダリングすると、コンソールには以下が出力されます。

Effect executed
Cleanup executed
Effect executed

つまり「実行 → クリーンアップ → 再実行」というシーケンスが走るのです。これは React が内部的に「このエフェクトは正しくクリーンアップされるか?」を検証するために再マウントしているためです。

3. なぜ「バグではない」のか

多くの開発者は「useEffect が 2 回呼ばれるなんておかしい」と感じます。

しかし本番では 1 回だけであり、StrictMode はあくまで「開発補助ツール」です。React チームは副作用の取り扱いをより安全にするために、この動作を導入しました。もし useEffect 内で正しくクリーンアップ処理をしていれば、2 回目の実行も問題なく耐えられる設計になります。

したがって、StrictMode 下で 2 回実行されることは「アプリの設計が副作用に強いかどうか」をチェックするテストの一環に過ぎないのです。

4. 実務での対処方法

それでは、実際の開発現場でこの挙動にどう対応すればよいのでしょうか。主な対処法を整理します。

  • 副作用の中で「初回だけ実行したい処理」がある場合は、状態管理フラグの利用で制御する
  • API リクエストは リトライや重複防止ロジックを組み込む
  • デバッグログやアナリティクス送信など、開発中に重複して困る処理は条件分岐で制御する
  • どうしても 1 回だけの実行を確認したいなら、一時的に StrictMode を外して挙動を確認する(ただし本番では外す必要はない)

API リクエストの重複防止例

useEffect(() => {
  let called = false;
  if (!called) {
    fetch('/api/data').then(res => res.json()).then(console.log);
    called = true;
  }
}, []);

このようにフラグや状態を使えば「実質 1 回だけ」に制御できます。

5. よくある誤解とアンチパターン

StrictMode 下でのダブル実行に対して、次のような誤解や誤対応が見られます。

  • 「React のバグだから無視していい」 → 実際は正しい仕様
  • 「本番でも 2 回実行される」 → 本番は 1 回のみ
  • 「StrictMode を外せば解決」 → 一時的には消えるが、本来の安全性チェックを放棄することになる
  • 「副作用をクリーンアップせず放置」 → リークやバグの温床

これらはすべて誤った対応です。むしろ、この仕様を前提に副作用を設計することが望ましいです。

6. 今後のベストプラクティス

React 18 以降、副作用の設計は「複数回実行されても安全であること」を前提にすべきです。

例えば、イベントリスナーを登録する場合は必ずクリーンアップを行う、API コールは冪等性を意識する、ログや状態更新は重複しても破綻しないようにする、といった工夫が求められます。

これにより、本番環境でも安定した挙動を保ちつつ、将来の React の変更にも柔軟に対応できます。また、StrictMode は将来的により多くのシナリオで利用される可能性があるため、開発段階から有効にしておくことを推奨します。

まとめ

React 18 の StrictMode で useEffect が 2 回実行されるのは「バグではなく仕様」です。副作用の正しいクリーンアップを促し、より安全なコードを書くための仕組みと理解しましょう。実務ではフラグや冪等性を意識した設計を行い、警告やダブル実行を前向きに捉えることが重要です。本番では 1 回だけ実行されるため、過剰に恐れる必要はありません。

参考

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