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

ReactのuseStateとuseEffectでよくあるエラーと解決方法ガイド!初心者でもわかるReactフック

useStateとuseEffectでよくあるエラーと解決方法
useStateとuseEffectでよくあるエラーと解決方法

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

生徒

「ReactでuseStateやuseEffectを使うとエラーが出ることがあるんですけど、どうしたらいいですか?」

先生

「よくあるエラーにはパターンがあります。順番に見て解決方法も紹介します。」

生徒

「具体的にはどんなエラーですか?」

先生

「たとえば、useStateで初期値を設定していないとundefinedのエラーが出たり、useEffectで依存配列を間違えると無限ループになったりします。」

生徒

「無限ループって何ですか?」

先生

「コンポーネントが何度も繰り返しレンダリングされてしまう状態のことです。useEffectの依存配列を正しく設定することで防げます。」

1. useStateでよくあるエラーと解決方法

1. useStateでよくあるエラーと解決方法
1. useStateでよくあるエラーと解決方法

useStateは状態を管理するフックですが、初期値を設定していなかったり、状態を直接変更しようとするとエラーや予期せぬ挙動が発生します。

  • 初期値がない場合のundefinedエラー
    解決策:useStateには必ず初期値を設定する
  • 状態を直接変更してしまう
    解決策:setState関数を使って更新する

import React, { useState } from "react";

function App() {
  const [count, setCount] = useState(0); // 初期値を必ず設定

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

export default App;
(ボタンを押すとカウントが増えて、状態が正しく更新されます)

2. useEffectでよくあるエラーと解決方法

2. useEffectでよくあるエラーと解決方法
2. useEffectでよくあるエラーと解決方法

useEffectは副作用を扱うフックですが、依存配列の指定や非同期処理の扱いを間違えるとエラーや無限ループが発生します。

  • 依存配列の指定ミスで無限ループ
    解決策:必要な値だけを依存配列に入れる
  • 非同期関数を直接useEffectに渡す
    解決策:関数内で非同期関数を定義して呼び出す

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

function App() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch("https://api.example.com/data");
      const result = await response.json();
      setData(result);
    }
    fetchData();
  }, []); // 空配列で初回のみ実行

  return <div>データ: {JSON.stringify(data)}</div>;
}

export default App;
(初回レンダリング時にデータを取得し、画面に表示されます)

3. よくあるエラー

3. よくあるエラー
3. よくあるエラー

ReactでuseStateやuseEffectを使うときに注意したいポイントは次の通りです。

  • useStateは初期値を必ず設定する
  • 状態を直接変更せず、setState関数で更新する
  • useEffectの依存配列は正しく設定する
  • 非同期処理は関数内で定義して呼び出す
  • コンポーネント削除時にはクリーンアップを意識する

これらを意識するだけで、多くのエラーを防ぐことができます。

4. useEffectのクリーンアップが必要なケースと対処方法

4. useEffectのクリーンアップが必要なケースと対処方法
4. useEffectのクリーンアップが必要なケースと対処方法

useEffectを使用していると、コンポーネントが削除されたときに古い処理が残ってしまい、メモリリークや意図しない挙動につながることがあります。 特にイベントリスナーやタイマー、外部APIとの接続などを使う場合には、クリーンアップを正しく実行する必要があります。

useEffectの返り値として関数を返すことで、コンポーネントのアンマウント時にクリーンアップが行われます。 この仕組みを理解しておくと、複雑な副作用を扱う場合でも安全に処理を管理できます。


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

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

  useEffect(() => {
    const timer = setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);

    return () => clearInterval(timer); // クリーンアップ
  }, []);

  return <div>カウント: {count}</div>;
}

export default TimerComponent;

クリーンアップ処理を忘れるとタイマーが残り続けたり、メモリの無駄遣いにつながるため、しっかりと意識しておくことが重要です。

5. useStateでオブジェクトや配列を扱うときの注意点

5. useStateでオブジェクトや配列を扱うときの注意点
5. useStateでオブジェクトや配列を扱うときの注意点

useStateは文字列や数値だけでなく、オブジェクトや配列も管理できます。しかし、これらを更新するときに 直接変更してしまうと再レンダリングが行われなかったり、予期せぬデータ破損が起きることがあります。

オブジェクトや配列を更新するときは、スプレッド構文を使って新しいデータ構造を作成することが大切です。 Reactでは「不変性」を保つことで、変更を正しく検知し、コンポーネントが更新されるようになっています。


import React, { useState } from "react";

function UserList() {
  const [users, setUsers] = useState(["太郎", "花子"]);

  const addUser = () => {
    setUsers([...users, "新しいユーザー"]); // 配列をコピーして追加
  };

  return (
    <div>
      <ul>
        {users.map((user, index) => (
          <li key={index}>{user}</li>
        ))}
      </ul>
      <button onClick={addUser}>追加</button>
    </div>
  );
}

export default UserList;

オブジェクトや配列を扱う場面は多いため、不変性を意識した更新方法を習得しておくと、エラーを大幅に減らすことができます。

6. useEffectの依存配列でよく起こる意図しない再レンダリング

6. useEffectの依存配列でよく起こる意図しない再レンダリング
6. useEffectの依存配列でよく起こる意図しない再レンダリング

useEffectを使うとき、依存配列に入れた値によっては意図しないタイミングで処理が再実行されることがあります。 特にオブジェクトや関数を依存配列に入れる場合、毎回新しい参照が生成されるため、無限ループの原因になることがあります。

このような場合は、useCallbackやuseMemoを使って関数や計算結果をメモ化したり、依存しない値は配列に含めない工夫が必要です。 適切に依存配列を管理することで、不要な再レンダリングを防ぎ、パフォーマンスの最適化にもつながります。


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

function FetchComponent() {
  const [query, setQuery] = useState("react");

  const fetchData = useCallback(async () => {
    const response = await fetch(`https://api.example.com?q=${query}`);
    const result = await response.json();
    console.log(result);
  }, [query]); // queryを依存にする

  useEffect(() => {
    fetchData();
  }, [fetchData]); // メモ化された関数を依存に指定

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="検索ワード"
      />
    </div>
  );
}

export default FetchComponent;

意図しない再実行や無限ループは依存配列の設定に原因があることが多いため、値の参照の仕組みを理解しながら設計することが大切です。

まとめ

まとめ
まとめ

ReactのuseStateやuseEffectを使うときには、見落としやすいポイントが多く、ちょっとした記述の違いが大きなエラーや予期せぬ動作につながることがある。とくに状態管理と副作用処理は、アプリケーションの動きに深く関わるため、初期値の設定や依存配列の扱い、コンポーネントのアンマウント時に行うクリーンアップ処理など、基本的な考え方をしっかり理解しておくことが重要となる。これらの仕組みを適切に扱えるようになることで、Reactアプリケーションが安定し、レンダリングの無限ループやデータの不整合、意図しない更新などのトラブルを防ぐことにつながる。

また、useStateで配列やオブジェクトを扱う場面が増えると、Reactの不変性という概念を意識しなければならない状況も多くなる。不変性を守ることで、Reactが変更を検知し、再レンダリングを適切にトリガーすることができる。スプレッド構文を使った新しいオブジェクトや配列の生成は、その最も一般的な方法であり、実際のプロジェクトでも頻繁に利用されるテクニックである。これらを自然に使いこなせるようになると、複雑な状態管理でも迷わず扱えるようになり、コードの見通しも良くなる。

一方でuseEffectの扱いもまた繊細で、特に依存配列をどのように設定するかがコンポーネントの動作に大きく影響を与える。依存配列の設定を誤ると、無限ループのようにレンダリングが止まらなくなる問題が発生することがあるため、どの値がeffectを再実行するきっかけになるのかを理解しておく必要がある。また、非同期処理をuseEffectに直接書くのではなく、内部で非同期関数を定義して呼び出す方法も、エラーを避けるためには欠かせないポイントである。さらに、fetchなどのAPI通信を行う際には、更新の競合が起きないように注意し、必要に応じてクリーンアップ処理を取り入れることも求められる。

こうしたReact特有のルールや注意点は、使い慣れてくるほど自然に理解できるようになるが、初心者にとっては最初の壁になりやすい。しかし、エラーの原因には一定のパターンがあり、その背景にある仕組みを知ることで、トラブルの多くは解決できる。たとえば「依存配列の指定ミス」「不変性を保たない更新」「クリーンアップの不足」など、気をつけるべき観点がわかっていれば、エラーが発生しても冷静に原因を探り対処できるようになる。

サンプルプログラムで全体を振り返る


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

function SummarySample() {
  const [text, setText] = useState("");
  const [list, setList] = useState([]);
  const [count, setCount] = useState(0);

  const addItem = () => {
    setList([...list, text]);
    setText("");
  };

  const updateCount = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);

  useEffect(() => {
    const timer = setInterval(updateCount, 1000);
    return () => clearInterval(timer); // クリーンアップ
  }, [updateCount]);

  return (
    <div className="summary-box">
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="入力してください"
      />
      <button onClick={addItem}>追加</button>

      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>

      <div>自動カウント: {count}</div>
    </div>
  );
}

export default SummarySample;

このサンプルでは、useStateで文字列と配列を管理し、useCallbackで関数を安定させ、useEffectでタイマー処理を動かしている。ひとつひとつのフックの特性を理解することで、複数の処理が絡み合うような場合でも落ち着いて構築できる。また、副作用には必ず終わりがあり、クリーンアップが正しく行われることでコンポーネントの動作はより安全なものとなる。

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

生徒:「useStateとuseEffectのエラーって複雑に見えましたが、原因がわかると意外と整理できるんですね。」

先生:「そうですね。仕組みがわかると、エラーもヒントのように見えてきますよ。」

生徒:「クリーンアップの必要性もよく理解できました。タイマーやイベントを放置すると大変なんですね。」

先生:「その通りです。コンポーネントのライフサイクルを意識すると、より安全なコードが書けますよ。」

生徒:「依存配列の設定も慣れればスムーズにできそうです。うっかり無限ループにしないよう気を付けます。」

先生:「よく理解できていますね。使っていくうちに自然と身につくので、たくさん書いてみましょう。」

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

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

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

ReactでuseStateに初期値を設定しないとどんなエラーになりますか?

ReactのuseStateに初期値を設定しないと、undefinedのまま状態が使われてしまい、画面にエラーが表示されたり、期待した動作をしなくなります。必ず初期値を設定しましょう。
カテゴリの一覧へ
新着記事
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
ReactのuseStateとuseEffectでよくあるエラーと解決方法ガイド!初心者でもわかるReactフック
No.7
Java&Spring記事人気No7
React
create-react-appでReactプロジェクトを作成する手順を初心者向けに完全解説!
No.8
Java&Spring記事人気No8
React
Reactでファイルアップロードを実装する方法を解説!Fetch APIで画像やPDFを送る仕組みを初心者向けに紹介