0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

useEffectにasyncはNG?安全なデータ取得の書き方を徹底解説

Last updated at Posted at 2025-06-24

はじめに

Reactでデータフェッチを行うとき、useEffectasync/awaitを組み合わせるのはよくあるパターンです。

しかし、次のように書いたことはありませんか?

React.useEffect(async () => {
  const data = await fetchData({ query, page, tag });
  setResults(data.results);
}, [query, page, tag]);

実はこれ、非推奨の書き方です。

なぜ useEffect(async () => {}) がダメなのか?

useEffectに渡す関数は、Reactの仕様上、

  • undefined を返す
  • または「クリーンアップ関数」を返す必要があります。

ところが、async関数は常に Promise を返すため、Reactが期待している戻り値の形式に合いません

その結果…

  • クリーンアップが正しく動作しない
  • 将来のReactのアップデートで警告やバグの原因になる

といった問題が起こる可能性があります。

✅ 正しい書き方:async 関数を定義する

React.useEffect(() => {
  const handleFetchData = async () => {
    setLoading(true);
    const data = await fetchData({ query, page, tag });
    setResults(data.results);
    setLoading(false);
  };

  handleFetchData();
}, [query, page, tag]);

このように、非同期関数は useEffect の中で定義して実行するのが基本です。

async関数を useEffect の外に出すべき?

一見、handleFetchData を外に出したほうが「きれい」になりそうに見えます。でも、それには注意が必要です。

❌ 原則:依存変数がある場合は外に出さないほうが良い

const handleFetchData = async () => {
  const data = await fetchData({ query, page, tag }); // ❌ query などが stale になる可能性
  setResults(data.results);
};

useEffect(() => {
  handleFetchData(); // ❌ 依存配列だけ更新されても中身が古いまま
}, [query, page, tag]);

このコードは、handleFetchDataquery, page, tag をクロージャとしてキャプチャしてしまうため、値が古くなる可能性があります。

✅ 例外:再利用したいときは useCallback + 依存配列で管理

たとえば「再取得」ボタンを用意したい場合:

const handleFetchData = useCallback(async () => {
  setLoading(true);
  const data = await fetchData({ query, page, tag });
  setResults(data.results);
  setLoading(false);
}, [query, page, tag]);

useEffect(() => {
  handleFetchData();
}, [handleFetchData]);

return <button onClick={handleFetchData}>再取得</button>;

これは、

handleFetchData が変わったときだけ、呼び出す

という意味の useEffect です。分解してみると:

「依存の値 ( query, page, tag ) が変わった → 関数 ( handleFetchData ) が変わった → 副作用 ( useEffect ) 実行」という意図した挙動を実現

  1. handleFetchData() は API を呼ぶなどの副作用(=副作用関数)です。

  2. useEffect の依存配列 [handleFetchData] に注目。

    • これは「handleFetchData中身が変わったときに再実行する」設定です。
    • 中身が変わるというのは、useCallback で依存が変わって再生成されたときのこと。

メリット

  • useCallback を使えば関数の再生成を防げる
  • query, page, tag を依存に入れることで常に最新値が使われる

まとめ

  • useEffectには直接 async 関数を渡さない!
  • handleFetchData は中で定義 or useCallbackで管理
  • Reactの再レンダーや依存関係を理解したうえで、関数のスコープ設計を考えるのが大事
シチュエーション 推奨されるパターン
useEffect内だけで使う useEffectの中で定義
他のイベントでも使いたい useCallbackでメモ化して外に出す
依存がない / グローバル関数 外に出してもOK
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?