ReactのuseEffect二重実行問題とは?StrictModeの影響を初心者向けに解説
生徒
「ReactのuseEffectを使ったら、処理が二回実行されてしまいました。これはバグなんですか?」
先生
「それはReactの仕組みによるものです。特にStrictModeを有効にしていると、useEffectが二重に実行されるように見えることがあります。」
生徒
「StrictModeってなんですか?そして、なぜ二回動くんですか?」
先生
「いい質問ですね。これを理解すると、Reactでの非同期処理や副作用の扱い方がずっと分かりやすくなりますよ。」
1. useEffectとは?初心者でもわかる基本
まず、useEffectとはReactのフックのひとつで、コンポーネントが表示されたあとに何か処理をしたいときに使います。例えば、Webページを開いたときにデータを取得したり、画面のタイトルを変更したりするのに使われます。
「副作用」という言葉もよく出てきます。これは、画面に表示する以外の処理のことを指します。例えば、外部サーバーにデータを取りに行ったり、ブラウザのAPIを使ったりするのが副作用です。
import React, { useEffect } from "react";
function App() {
useEffect(() => {
console.log("コンポーネントが表示されました");
}, []);
return <h1>こんにちはReact</h1>;
}
export default App;
2. useEffectが二回実行される理由
Reactの開発環境(特にStrictModeが有効のとき)では、useEffectが二回動くように見えることがあります。これはReactが「本当に副作用が安全に書かれているか」をチェックするために、わざと二回呼び出しているのです。
たとえるなら、新しい電化製品を工場から出荷する前に「テスト動作」をするのと似ています。一度動かして問題がないか確認することで、ユーザーが使うときに安心できるのです。
本番環境(ユーザーに公開されたとき)では、通常は一回だけ実行されます。なので、二回実行されるのは「開発モード特有の挙動」です。
3. StrictModeとは?
StrictMode(ストリクトモード)は、Reactが提供するチェック機能のひとつです。コードの中に「将来問題を起こしそうな書き方」がないかを確認してくれます。Reactをはじめたばかりの人にとっては少し戸惑うかもしれませんが、実際には安全にアプリを作るために役立つ仕組みです。
StrictModeを使うと、useEffectの実行が二重にチェックされます。そのため「二回実行された!バグだ!」と思いやすいのですが、これは仕様であり、安心してよい挙動です。
4. 二重実行に対応するベストプラクティス
では、二回実行されても困らないようにするにはどうすればいいのでしょうか。ポイントは「副作用を安全に書く」ことです。
- 同じ処理が二回呼ばれても問題が起きないように書く
- クリーンアップ関数(後片付け処理)をしっかり書く
- 状態を正しく管理する(例えば、データ取得中に重複リクエストをしない工夫)
import React, { useEffect, useState } from "react";
function App() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((res) => res.json())
.then((result) => {
if (isMounted) {
setData(result);
}
});
return () => {
isMounted = false;
};
}, []);
return (
<div>
<h1>データ表示</h1>
{data ? <p>{data.title}</p> : <p>読み込み中...</p>}
</div>
);
}
export default App;
5. 初心者が混乱しやすいポイント
多くの初心者が「なぜ二回動くのか?」「バグなのか?」と迷います。しかし、これはReactがわざとチェックしている動作なので焦らなくて大丈夫です。
逆に、この仕組みを知っておけば、将来大きなエラーを避けやすくなります。最初は少し難しく感じても「二回動いても壊れないように書けばいい」と考えればシンプルです。
まとめ
ここまで、React開発における大きな関門の一つである「useEffectの二重実行」と「StrictMode(ストリクトモード)」の関係について詳しく解説してきました。Reactの学習を始めたばかりの頃は、コンソールログが2回出力されるだけで「自分のコードに致命的なミスがあるのではないか」と不安になるものです。しかし、今回学んだ通り、これはReactが意図的に用意した「安全装置」のようなものです。
useEffect二重実行の要点再チェック
ReactのStrictModeは、開発者が「マウント・アンマウント・再マウント」のサイクルを正しく扱えているかを検証します。これにより、メモリリークの原因となるイベントリスナーの消し忘れや、不適切な外部APIとの接続を未然に防ぐことができます。以下のポイントを意識することで、より高品質なReactアプリケーションを作成できるようになります。
- 開発環境のみの挙動:本番環境(ビルド後)では通常通り1回のみの実行となります。
- クリーンアップ関数の重要性:useEffect内でタイマーや購読を開始した場合は、必ず
return () => { ... }で解除を行いましょう。 - 冪等性(べきとうせい)の確保:「同じ処理を2回行っても結果が変わらない、または不具合が起きない」書き方を意識することが、モダンなReact開発の基本です。
実践的な実装サンプル:タイマーとクリーンアップ
例えば、1秒ごとにカウントアップする処理をuseEffectで書く場合、二重実行を考慮しないとタイマーが複数動いてしまい、カウントの進みが速くなってしまいます。これを正しく制御するコードを見てみましょう。
import React, { useState, useEffect } from "react";
function TimerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒ごとにカウントを増やすタイマーをセット
const timerId = setInterval(() => {
setCount((prev) => prev + 1);
console.log("タイマーが動いています");
}, 1000);
// クリーンアップ関数(ここが重要!)
// コンポーネントが外れる際や、二重実行のチェック時に古いタイマーを消去する
return () => {
clearInterval(timerId);
console.log("タイマーを停止しました");
};
}, []);
return (
<div className="p-3 border rounded bg-light">
<h3>経過時間: {count}秒</h3>
<p>このコンポーネントはStrictModeの影響を正しく受け流します。</p>
</div>
);
}
export default TimerComponent;
現場で役立つエラー回避のコツ
実際のプロジェクトでは、API通信を2回行いたくないケースも多々あります。その場合は、先ほど紹介したisMountedフラグを利用する手法や、AbortControllerを使って前のリクエストをキャンセルする方法が一般的です。また、React Query(TanStack Query)やSWRといったデータフェッチ用ライブラリを導入することで、こうした「二重実行」や「キャッシュ管理」の問題をライブラリ側に任せて、開発者はロジックに集中するという選択肢もあります。
Reactの進化とともに、開発者に求められる「お作法」は変化していますが、StrictModeが教えてくれるエラーの芽を摘んでいく作業こそが、バグの少ない堅牢なシステム作りへの近道です。最初は煩わしく感じるかもしれませんが、Reactからの「もっと良い書き方があるよ」というアドバイスだと捉えて、積極的に活用していきましょう。
生徒
「先生、ありがとうございました!最初はコンソールのログが2回出るのがバグだと思って必死に修正しようとしていました。でも、Reactがわざとテストしてくれていたんですね。」
先生
「その通りです。特に『クリーンアップ関数』を書く習慣をつけるために、この二重実行はとても良い練習台になります。タイマーやイベントリスナーを使い始めると、その大切さがもっと分かってきますよ。」
生徒
「確かに、後片付けを忘れるとメモリを無駄に使っちゃいますもんね。コードを書くときは、本番環境でどう動くかだけでなく、Reactが求めている『正しいライフサイクル』を意識してみます。」
先生
「素晴らしい意気込みですね!もしどうしても2回実行されるのが邪魔でデバッグしにくい時は、一時的に index.js や main.tsx にある <React.StrictMode> タグをコメントアウトするという手もあります。でも、基本的には有効にしたまま、警告が出ないコードを目指すのがプロへの近道です。」
生徒
「なるほど。まずはStrictModeに怒られないような、きれいなコードを書けるように頑張ります!API通信のキャンセル処理も、さっそく自分のプロジェクトに取り入れてみますね。」
先生
「その調子です。useEffectを使いこなせれば、Reactの表現力は一気に広がります。次は依存配列(dependency array)の最適化についても一緒に学んでいきましょう!」