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 回だけ実行されるため、過剰に恐れる必要はありません。
参考
- React 公式ドキュメント StrictMode https://react.dev/reference/react/StrictMode
- React 18 Release Notes https://react.dev/blog/2022/03/29/react-v18