カテゴリ: React 更新日: 2026/01/08

ReactのuseEffectで無限ループを防ぐ方法をやさしく解説!初心者向け

useEffectでの無限ループを防ぐ方法
useEffectでの無限ループを防ぐ方法

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

生徒

「先生、ReactでuseEffectを使ったら、画面が止まらなくなりました。どうしてでしょう?」

先生

「それは無限ループが発生している可能性があります。useEffectは依存配列の値が変わるたびに処理を繰り返します。」

生徒

「依存配列って何ですか?」

先生

「依存配列は、useEffectの処理を実行する条件となる値のリストです。ここに指定した値が変わると、useEffectの中の処理が再実行されます。」

生徒

「なるほど。でもどうやって無限ループを防ぐんですか?」

先生

「依存配列を正しく設定することと、状態更新のタイミングに注意することで防げます。具体的に例を見てみましょう。」

1. useEffectで無限ループが発生する理由

1. useEffectで無限ループが発生する理由
1. useEffectで無限ループが発生する理由

ReactのuseEffectは、画面が描画されたあとに「追加でやってほしい処理(副作用)」を実行するための仕組みです。ここで注意したいのが、useEffectの中でsetState(useStateの更新)を行うと、状態が変わったことで再レンダーが起き、条件によってはuseEffectがもう一度動いてしまう点です。さらに、useEffectの中で毎回状態を更新していると、レンダー → useEffect → 状態更新 → レンダー…という流れが止まらず、useEffectの無限ループになります。特に「毎回setStateする」「毎回新しい値を作って入れる」ような書き方は、初心者がつまずきやすいポイントです。


import React, { useState, useEffect } from "react";

function App() {
  const [message, setMessage] = useState("読み込み中...");

  useEffect(() => {
    // 何も条件がない状態更新は、繰り返し実行されやすい
    setMessage("データを読み込みました");
  });

  return <p>{message}</p>;
}

export default App;
(画面の表示が何度も更新され続けたり、動作が重くなったりします。原因は、描画後にuseEffectが動き、そこで状態を更新して再描画が起き、またuseEffectが動く流れが続くためです)

2. 依存配列を使って無限ループを防ぐ

2. 依存配列を使って無限ループを防ぐ
2. 依存配列を使って無限ループを防ぐ

依存配列とは、useEffectの第2引数に渡す角カッコ[]の中身のことです。ここに値を書くと、「その値が変わったときだけuseEffectを動かしてね」という合図になります。逆に、依存配列を書かないまま状態更新(setState)をすると、再レンダーのたびにuseEffectが実行されやすくなり、結果として無限ループにつながります。初心者のうちは、まず「useEffectは毎回動く可能性がある」「だから条件(依存配列)を付けて回数を絞る」と覚えるとスッキリします。

たとえば、ボタンを押したときだけ数が増えるような画面なら、依存配列にcountを入れておくことで、countが変わったタイミングだけ処理が走ります。これだけでも「知らないうちに何回も実行される」事故をかなり減らせます。


import React, { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // countが変わったときだけ、ここが実行される
    console.log("カウントが変わりました:", count);
  }, [count]);

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>カウントを増やす</button>
    </div>
  );
}

export default App;
(「カウントを増やす」を押すたびに表示が更新され、同じタイミングでコンソールに「カウントが変わりました: 数字」が出ます。依存配列にcountを書いているので、関係ない再レンダーではuseEffectが何度も走りません)

3. 空の依存配列で初回のみ実行

3. 空の依存配列で初回のみ実行
3. 空の依存配列で初回のみ実行

依存配列を空([])にすると、useEffectはコンポーネントが画面に表示された「最初の1回だけ」実行されます。これは「ページを開いたときに一度だけ行いたい処理」にとても向いています。たとえば、最初のあいさつ文を表示したり、初期データを読み込んだりする場面です。依存配列が空であれば、その後に状態が何度変わってもuseEffectは再実行されないため、setStateを書いても無限ループになりません。初心者の方は「何も指定しなければ毎回動く」「空配列なら最初だけ動く」と覚えると混乱しにくくなります。


import React, { useState, useEffect } from "react";

function App() {
  const [message, setMessage] = useState("");

  useEffect(() => {
    // 画面が最初に表示されたときだけ実行される
    setMessage("こんにちは!");
  }, []); // 空の依存配列

  return <p>{message}</p>;
}

export default App;
(画面を開いたときに「こんにちは!」と表示されます。その後に状態が変わっても、このuseEffectは再び実行されません)

4. 状態更新の条件を工夫して無限ループを回避

4. 状態更新の条件を工夫して無限ループを回避
4. 状態更新の条件を工夫して無限ループを回避

無限ループを防ぐもう一つの方法は、状態更新の前に条件を入れることです。これにより、本当に必要な場合だけ状態を更新できます。


useEffect(() => {
  if (count < 5) { // 条件を追加
    setCount(count + 1);
  }
}, [count]);
(countが5未満のときだけ状態を更新するため、無限ループにならずに処理が停止します)

5. useEffectで関数やオブジェクトを依存にする場合の注意

5. useEffectで関数やオブジェクトを依存にする場合の注意
5. useEffectで関数やオブジェクトを依存にする場合の注意

配列やオブジェクト、関数などを依存配列に直接入れると、Reactは毎回新しい参照と判断して再実行することがあります。無限ループを防ぐには、useCallbackやuseMemoを使って参照を安定させると安全です。


import React, { useState, useEffect, useCallback } from "react";

function App() {
  const [count, setCount] = useState(0);

  const logCount = useCallback(() => {
    console.log("カウントは:", count);
  }, [count]);

  useEffect(() => {
    logCount();
  }, [logCount]); // 関数の参照を安定させて依存

  return (
    

カウント: {count}

); } export default App;
(useCallbackで関数の参照を安定させることで、無限ループを回避できます)

6. 無限ループになりやすいパターンを知ろう

6. 無限ループになりやすいパターンを知ろう
6. 無限ループになりやすいパターンを知ろう

代表的な無限ループパターンは、useEffect内でsetStateを無条件に実行するケースや、依存配列を指定せずに状態更新を行うケースです。これらは副作用と状態更新の関係を正しく理解すれば防げます。小さな条件分岐や依存配列の設定で、無限ループを安全に回避できます。

7. デバッグのポイント

7. デバッグのポイント
7. デバッグのポイント

無限ループが発生した場合は、useEffectの中でconsole.logを使ってどの値が何回変わっているか確認します。依存配列に指定した値や関数が毎回新しい参照になっていないかもチェックすると原因を特定しやすくなります。

8. 初心者でも意識しておきたいコツ

8. 初心者でも意識しておきたいコツ
8. 初心者でも意識しておきたいコツ
  • 状態を更新する処理は本当に必要な場合だけ行う
  • 依存配列を正しく設定して再実行を制御する
  • 関数やオブジェクトを依存にする場合はuseCallbackやuseMemoを使う
  • 無限ループになったらconsole.logで実行回数や値を確認する

まとめ

まとめ
まとめ

useEffectと無限ループの仕組みを振り返ろう

この記事では、ReactのuseEffectでなぜ無限ループが発生するのか、その原因と対策を段階的に確認してきました。useEffectはコンポーネントが描画されたあとに処理を実行するための便利な仕組みですが、状態更新と組み合わせることで思わぬ挙動を引き起こすことがあります。特に、useEffectの中でsetStateを無条件に実行すると、再レンダーが発生し、その結果として再びuseEffectが実行される流れが止まらなくなります。この仕組みを理解していないと、画面が固まったり、動作が極端に重くなったりする原因になります。

重要なポイントは、useEffectが「いつ」「どんな条件で」実行されるのかを正しく把握することです。依存配列を指定しない場合、useEffectは再レンダーのたびに実行されます。一方で、依存配列を設定すれば、その中の値が変わったときだけ実行されるようになり、不要な処理の繰り返しを防ぐことができます。さらに、空の依存配列を使えば、画面が最初に表示されたときの一度だけ実行されるため、初期処理やデータ取得などにも安心して利用できます。

状態更新と依存配列の関係を整理する

useEffectで無限ループを防ぐためには、状態更新の設計も非常に大切です。毎回同じ値をセットしていたり、条件を付けずに状態を変更していると、Reactは「状態が変わった」と判断し、再レンダーを繰り返します。そのため、状態更新の前に条件分岐を入れることで、本当に必要なタイミングだけ状態を変更するようにすると安全です。こうした工夫は、初心者が最初につまずきやすいポイントでもありますが、一度理解するとReactの動きがぐっと分かりやすくなります。


useEffect(() => {
  if (count < 3) {
    setCount(count + 1);
  }
}, [count]);

このように条件を入れることで、特定の回数や状態に達したら処理を止めることができ、無限ループを確実に回避できます。useEffectは強力な反面、状態更新との組み合わせには常に注意が必要だということを覚えておきましょう。

関数やオブジェクトを扱うときの注意点

記事の後半では、useEffectの依存配列に関数やオブジェクトを指定する場合の注意点についても触れました。Reactでは、関数やオブジェクトは再レンダーのたびに新しい参照として扱われることがあります。その結果、見た目は同じ処理でも「値が変わった」と判断され、useEffectが何度も実行されてしまうケースがあります。この問題を防ぐために、useCallbackやuseMemoを使って参照を安定させる方法を学びました。

初心者のうちは少し難しく感じるかもしれませんが、「依存配列に入れるものは、本当に変化を監視したいものだけにする」という考え方を持つだけでも、無限ループのリスクは大きく下げられます。console.logを使ったデバッグとあわせて、実行回数や値の変化を確認する習慣を身につけることも大切です。

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

生徒

「useEffectって便利だけど、何も考えずに使うと無限ループになる理由がよく分かりました。特に、setStateを毎回実行してしまうのが危ないんですね。」

先生

「その通りです。useEffectは状態と強く結びついているので、依存配列と状態更新の関係を意識することがとても大切です。まずは空の依存配列や、必要な値だけを指定するところから慣れていきましょう。」

生徒

「条件を付けて状態を更新すれば、無限ループを止められるのも理解できました。console.logで確認するのも、原因を探すのに役立ちそうですね。」

先生

「とても良い視点です。Reactでは、なぜ再レンダーされたのかを考える癖をつけると、バグに強くなります。useEffectの動きが分かれば、実務でも安心してコードを書けるようになりますよ。」

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

ReactのuseEffectで無限ループが起きる原因は何ですか?

useEffect内でsetStateを無条件に実行すると、状態更新→再レンダー→useEffect再実行の連続となり、無限ループが発生します。依存配列や条件分岐が必要です。
カテゴリの一覧へ
新着記事
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のカスタムフックの作り方を完全ガイド!再利用可能なロジックを切り出す仕組み
No.5
Java&Spring記事人気No5
React
ReactでFetch APIのローディング状態を管理する方法|初心者にもわかる解説
No.6
Java&Spring記事人気No6
React
create-react-appでReactプロジェクトを作成する手順を初心者向けに完全解説!
No.7
Java&Spring記事人気No7
React
ReactのuseStateとuseEffectでよくあるエラーと解決方法ガイド!初心者でもわかるReactフック
No.8
Java&Spring記事人気No8
React
Reactでファイルアップロードを実装する方法を解説!Fetch APIで画像やPDFを送る仕組みを初心者向けに紹介