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

ReactのuseEffectと非同期処理(async/await)の注意点を初心者向けに解説

useEffectと非同期処理(async/await)の注意点
useEffectと非同期処理(async/await)の注意点

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

生徒

「先生、ReactのuseEffectで非同期処理を使いたいんですが、普通にasync/awaitを書いても大丈夫ですか?」

先生

「実は直接useEffectにasyncをつけるのはおすすめできません。理由を順番に説明します。」

生徒

「どうしてですか?」

先生

「useEffectの関数はクリーンアップ機能も持っていて、Promiseを返すとReactが正しく扱えない場合があるからです。」

生徒

「じゃあ、どうやって非同期処理を書けばいいんですか?」

先生

「useEffectの中でasync関数を定義して呼び出すのが安全です。」

1. useEffectで非同期処理を安全に使う方法

1. useEffectで非同期処理を安全に使う方法
1. useEffectで非同期処理を安全に使う方法

ReactのuseEffectで非同期処理を行う場合、直接asyncをつけるのではなく、useEffectの中にasync関数を作って呼び出すようにします。useEffectは本来「必要ならクリーンアップ関数を返す」仕組みを持っているため、Promiseをそのまま返してしまう形は相性がよくありません。

少し言い換えると、useEffectは「ここで副作用を始めてね」という場所で、非同期処理は「あとで結果が返ってくる処理」です。だから、useEffectの中で非同期処理を担当する関数を分けておくと、流れが整理されて読みやすくなります。


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

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

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
      const result = await response.json();
      setData(result);
    };
    fetchData();
  }, []);

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

export default App;
(画面に取得したデータが表示されます。useEffectの中でasync関数を定義して呼び出すことで、非同期処理を安全に書けます)

まずはこの形を覚えておくと、useEffectとasync/awaitを組み合わせるときに迷いにくくなります。「useEffectの中でasync関数を作る → その関数を呼ぶ」という手順が、基本の型だと思って大丈夫です。

2. クリーンアップ処理と非同期処理の注意点

2. クリーンアップ処理と非同期処理の注意点
2. クリーンアップ処理と非同期処理の注意点

useEffectには「後片付け(クリーンアップ)」のための関数を返せる仕組みがあります。非同期処理は結果が返ってくるまで時間がかかることがあるので、その間に画面(コンポーネント)が消えてしまうと、返ってきた結果を使ってsetStateしようとしてエラーや警告の原因になります。

特にありがちなのが、ページ移動や表示の切り替えでコンポーネントがアンマウントされたあとに、API通信が完了してしまうケースです。そこで「まだ表示中なら更新してOK、もう消えていたら更新しない」という判定を入れておくと安心です。

一番シンプルな方法は、フラグ(ここではisMounted)を用意して、クリーンアップでfalseに切り替えるやり方です。


useEffect(() => {
  let isMounted = true;

  const fetchData = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
    const result = await response.json();
    if (isMounted) {
      setData(result);
    }
  };

  fetchData();

  return () => {
    isMounted = false;
  };
}, []);
(画面から消えたあとに通信が終わっても、isMountedがfalseならsetDataしないので安全です)

この考え方を覚えておくと、useEffectと非同期処理を組み合わせたときに起きやすい「表示が変わったあとに更新が走る」トラブルを減らせます。

3. async/awaitを使うときのポイント

3. async/awaitを使うときのポイント
3. async/awaitを使うときのポイント

useEffectとasync/awaitを一緒に使うときは、いくつか意識しておきたい基本ルールがあります。ここを押さえておくだけで、「なぜか動かない」「警告が出る」といったトラブルをかなり減らせます。

  • useEffectに直接asyncをつけない
    useEffectはクリーンアップ関数を返す前提の仕組みなので、asyncを直接つけるとPromiseが返ってしまい、意図しない挙動につながります。
  • 非同期処理はuseEffectの中で関数として定義する
    async関数を中で作って呼び出すことで、処理の流れが整理され、あとから読み返したときも理解しやすくなります。
  • 処理が長引く可能性を意識する
    通信や待ち時間がある処理は、画面が切り替わる可能性を前提に考え、クリーンアップとセットで設計するのが安全です。
  • エラーは想定内として扱う
    非同期処理は失敗する前提で考え、try/catchを使ってアプリ全体が止まらないようにします。

初心者のうちは「useEffectは流れを管理する場所」「async/awaitは時間のかかる処理」と役割を分けて考えると混乱しにくくなります。まずは安全な書き方を習慣にすることが大切です。

4. エラーハンドリングの例

4. エラーハンドリングの例
4. エラーハンドリングの例

useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
      const result = await response.json();
      setData(result);
    } catch (error) {
      console.error("データ取得に失敗しました", error);
    }
  };
  fetchData();
}, []);
(fetchでエラーが発生してもアプリがクラッシュせず、エラーをconsoleに表示できます)

5. ポイント整理

5. ポイント整理
5. ポイント整理
  • useEffectと非同期処理を組み合わせるときは直接asyncを使わず、内部で定義した関数を呼び出す
  • コンポーネントがアンマウントされる場合に備えてクリーンアップフラグを用意する
  • try/catchでエラーを安全に処理する
  • 非同期処理でsetStateを行うときは常に前回の状態やマウント状態を意識する

6. 依存配列と非同期処理を組み合わせるときの考え方

6. 依存配列と非同期処理を組み合わせるときの考え方
6. 依存配列と非同期処理を組み合わせるときの考え方

useEffectで非同期処理を使うときは、依存配列の指定にも注意が必要です。依存配列に値を入れるということは、「その値が変わったら非同期処理をやり直す」という意味になります。

例えば、検索キーワードが変わるたびにAPI通信をしたい場合、そのキーワードを依存配列に含めます。逆に、初回表示時に一度だけデータを取得したいなら、空配列を指定します。


useEffect(() => {
  const fetchData = async () => {
    const response = await fetch(url);
    const result = await response.json();
    setData(result);
  };
  fetchData();
}, [url]);
(urlが変わったタイミングで非同期処理が再実行され、最新のデータが取得されます)

「いつ再取得したいのか」を先に考えてから依存配列を書くと、無駄な通信や意図しない再実行を防ぎやすくなります。

7. 非同期処理中のローディング状態を管理する

7. 非同期処理中のローディング状態を管理する
7. 非同期処理中のローディング状態を管理する

非同期処理では、データが取得できるまでの「待ち時間」が発生します。その間、何も表示されないとユーザーは不安になります。そこで、ローディング状態をstateで管理する方法がよく使われます。


useEffect(() => {
  const fetchData = async () => {
    setLoading(true);
    const response = await fetch(url);
    const result = await response.json();
    setData(result);
    setLoading(false);
  };
  fetchData();
}, []);
(データ取得中は「読み込み中…」などの表示に切り替えることができます)

このように状態を分けて管理することで、非同期処理の流れが分かりやすくなり、画面の挙動も安定します。

8. 非同期処理でよくある勘違い

8. 非同期処理でよくある勘違い
8. 非同期処理でよくある勘違い

初心者がよく勘違いしやすいのが、「useEffectの中は上から順番に必ず同期的に動く」という考え方です。実際には、awaitの位置で処理は一時停止し、その後に結果が返ってきます。

そのため、非同期処理の直後にstateの値を使おうとすると、まだ更新されていないことがあります。こうした場合は、取得した結果を直接使うか、別のuseEffectで処理を分けると安全です。


useEffect(() => {
  const fetchData = async () => {
    const result = await getData();
    setData(result);
  };
  fetchData();
}, []);
(非同期処理の完了を待ってからstateを更新することで、予期しない動作を防げます)

「非同期=すぐ結果が返らない」という前提を意識することが、useEffectとasync/awaitを安全に使うコツです。

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

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

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

ReactのuseEffectでasync/awaitを直接使ってはいけないのはなぜ?

ReactのuseEffectはクリーンアップ処理も行う関数ですが、async関数はPromiseを返すため、Reactが期待する返り値の形式と合わず不具合の原因になります。
カテゴリの一覧へ
新着記事
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のイベントハンドリングのアンチパターンまとめ!初心者でもわかる注意点
No.3
Java&Spring記事人気No3
React
ReactとTypeScriptの環境構築をやさしく解説!Viteとtsconfigの設定も丁寧に紹介
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を送る仕組みを初心者向けに紹介