カテゴリ: React 更新日: 2026/02/09

ReactのuseEffect二重実行問題とは?StrictModeの影響を初心者向けに解説

useEffectの二重実行問題とStrictModeの影響
useEffectの二重実行問題とStrictModeの影響

先生と生徒の会話形式で理解しよう

生徒

「ReactのuseEffectを使ったら、処理が二回実行されてしまいました。これはバグなんですか?」

先生

「それはReactの仕組みによるものです。特にStrictModeを有効にしていると、useEffectが二重に実行されるように見えることがあります。」

生徒

「StrictModeってなんですか?そして、なぜ二回動くんですか?」

先生

「いい質問ですね。これを理解すると、Reactでの非同期処理や副作用の扱い方がずっと分かりやすくなりますよ。」

1. useEffectとは?初心者でもわかる基本

1. useEffectとは?初心者でもわかる基本
1. useEffectとは?初心者でもわかる基本

まず、useEffectとはReactのフックのひとつで、コンポーネントが表示されたあとに何か処理をしたいときに使います。例えば、Webページを開いたときにデータを取得したり、画面のタイトルを変更したりするのに使われます。

「副作用」という言葉もよく出てきます。これは、画面に表示する以外の処理のことを指します。例えば、外部サーバーにデータを取りに行ったり、ブラウザのAPIを使ったりするのが副作用です。


import React, { useEffect } from "react";

function App() {
  useEffect(() => {
    console.log("コンポーネントが表示されました");
  }, []);

  return <h1>こんにちはReact</h1>;
}

export default App;
(画面に「こんにちはReact」と表示され、開いたときにコンソールに「コンポーネントが表示されました」と出力されます)

2. useEffectが二回実行される理由

2. useEffectが二回実行される理由
2. useEffectが二回実行される理由

Reactの開発環境(特にStrictModeが有効のとき)では、useEffectが二回動くように見えることがあります。これはReactが「本当に副作用が安全に書かれているか」をチェックするために、わざと二回呼び出しているのです。

たとえるなら、新しい電化製品を工場から出荷する前に「テスト動作」をするのと似ています。一度動かして問題がないか確認することで、ユーザーが使うときに安心できるのです。

本番環境(ユーザーに公開されたとき)では、通常は一回だけ実行されます。なので、二回実行されるのは「開発モード特有の挙動」です。

3. StrictModeとは?

3. StrictModeとは?
3. StrictModeとは?

StrictMode(ストリクトモード)は、Reactが提供するチェック機能のひとつです。コードの中に「将来問題を起こしそうな書き方」がないかを確認してくれます。Reactをはじめたばかりの人にとっては少し戸惑うかもしれませんが、実際には安全にアプリを作るために役立つ仕組みです。

StrictModeを使うと、useEffectの実行が二重にチェックされます。そのため「二回実行された!バグだ!」と思いやすいのですが、これは仕様であり、安心してよい挙動です。

4. 二重実行に対応するベストプラクティス

4. 二重実行に対応するベストプラクティス
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. 初心者が混乱しやすいポイント

5. 初心者が混乱しやすいポイント
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;
(画面に「経過時間: 0秒」から始まるカウントが表示されます。StrictMode環境では、最初に「タイマーが動いています」「タイマーを停止しました」「タイマーが動いています」という順でログが流れますが、最終的に動くタイマーは1つだけになり、正常に1秒ずつカウントされます)

現場で役立つエラー回避のコツ

実際のプロジェクトでは、API通信を2回行いたくないケースも多々あります。その場合は、先ほど紹介したisMountedフラグを利用する手法や、AbortControllerを使って前のリクエストをキャンセルする方法が一般的です。また、React Query(TanStack Query)やSWRといったデータフェッチ用ライブラリを導入することで、こうした「二重実行」や「キャッシュ管理」の問題をライブラリ側に任せて、開発者はロジックに集中するという選択肢もあります。

Reactの進化とともに、開発者に求められる「お作法」は変化していますが、StrictModeが教えてくれるエラーの芽を摘んでいく作業こそが、バグの少ない堅牢なシステム作りへの近道です。最初は煩わしく感じるかもしれませんが、Reactからの「もっと良い書き方があるよ」というアドバイスだと捉えて、積極的に活用していきましょう。

先生と生徒の振り返り会話

生徒

「先生、ありがとうございました!最初はコンソールのログが2回出るのがバグだと思って必死に修正しようとしていました。でも、Reactがわざとテストしてくれていたんですね。」

先生

「その通りです。特に『クリーンアップ関数』を書く習慣をつけるために、この二重実行はとても良い練習台になります。タイマーやイベントリスナーを使い始めると、その大切さがもっと分かってきますよ。」

生徒

「確かに、後片付けを忘れるとメモリを無駄に使っちゃいますもんね。コードを書くときは、本番環境でどう動くかだけでなく、Reactが求めている『正しいライフサイクル』を意識してみます。」

先生

「素晴らしい意気込みですね!もしどうしても2回実行されるのが邪魔でデバッグしにくい時は、一時的に index.jsmain.tsx にある <React.StrictMode> タグをコメントアウトするという手もあります。でも、基本的には有効にしたまま、警告が出ないコードを目指すのがプロへの近道です。」

生徒

「なるほど。まずはStrictModeに怒られないような、きれいなコードを書けるように頑張ります!API通信のキャンセル処理も、さっそく自分のプロジェクトに取り入れてみますね。」

先生

「その調子です。useEffectを使いこなせれば、Reactの表現力は一気に広がります。次は依存配列(dependency array)の最適化についても一緒に学んでいきましょう!」

カテゴリの一覧へ
新着記事
New1
React
Reactコンポーネントの再利用と分割を完全マスター!初心者でもわかるコンポーネント設計
New2
React
ReactでAxiosインターセプターの使い方を完全ガイド!初心者でもわかるリクエストとレスポンスの処理方法
New3
React
JSXの書き方!初心者でもわかるReactタグと属性の基本ルール解説
New4
React
ReactとDockerを使った開発環境構築の基本を徹底解説!初心者でもわかるReactとDockerの連携方法
人気記事
No.1
Java&Spring記事人気No1
React
ReactでonChangeイベントを使ってフォーム入力値を管理する方法を初心者向けに解説
No.2
Java&Spring記事人気No2
React
ReactとTypeScriptの環境構築をやさしく解説!Viteとtsconfigの設定も丁寧に紹介
No.3
Java&Spring記事人気No3
React
Reactのイベントハンドリングのアンチパターンまとめ!初心者でもわかる注意点
No.4
Java&Spring記事人気No4
React
ReactでFetch APIのローディング状態を管理する方法|初心者にもわかる解説
No.5
Java&Spring記事人気No5
React
Reactのカスタムフックの作り方を完全ガイド!再利用可能なロジックを切り出す仕組み
No.6
Java&Spring記事人気No6
React
create-react-appでReactプロジェクトを作成する手順を初心者向けに完全解説!
No.7
Java&Spring記事人気No7
React
Reactのフォーム処理でよくあるエラーと解決法を完全解説!初心者でも安心して学べるReact入門
No.8
Java&Spring記事人気No8
React
Reactでファイルアップロードを実装する方法を解説!Fetch APIで画像やPDFを送る仕組みを初心者向けに紹介