🐣

【備忘録】useEffect の中で即時関数を使う

に公開

はじめに

.thenを使うのが良くないと聞いたので、useEffect内で非同期処理を行う方法について調べた備忘録です。
勉強したてで調べながらなので、間違っているところがあれば教えていただけると幸いです。

即時関数を使う理由

即時関数は、関数を定義すると同時に実行する書き方。
よくasyncを組み合わせて、非同期処理を簡潔に書くために使われる。

なぜ使うのか?

1. awaitをトップレベルで使えない

通常のスクリプトのトップレベルではawaitは使えない。

// これはエラーになる
await fetchData();

でも、即時関数を使えば await を使える。

(async () => {
  const data = await fetchData();
  console.log(data);
})();

2. スコープを汚さない

即時関数内で変数を定義すると、その変数は関数スコープ内に閉じ込められるので、グローバルスコープを汚さない。

(() => {
  const secret = "これは外から見えない";
  console.log(secret);
})();
console.log(secret); // ❌ エラーになる

3. async関数をすぐに実行できる

async functionを定義してから実行するのではなく、即時関数を使えば関数を定義しつつ即実行できる。

(async () => {
  const response = await fetch("https://api.example.com");
  const data = await response.json();
  console.log(data);
})();

これなら.thenを使わずに非同期処理を書ける。

.thenを避ける理由

.thenはネストが深くなってしまいがちで、可読性が落ちることがある。

fetch("https://api.example.com")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => console.error(error));

これは.thenが続いてしまい、処理の流れがわかりにくくなる。

解決策 → async/await
async/awaitを使うと、同期的なコードのように書けるので可読性が上がる。

async function fetchData() {
  try {
    const response = await fetch("https://api.example.com");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

fetchData();

ただし、この方法だと fetchData() を別途呼び出さないといけない。
これを省略したい場合は 即時関数を使う。

(async () => {
  try {
    const response = await fetch("https://api.example.com");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
})();

使用例

このカスタムフックuseThreadsは、掲示板のスレッド一覧を取得し、React のuseStateを使って管理します。
コンポーネントがマウントされたタイミングでデータを取得し、threadsに格納します。

修正前

以下のコードでは、fetch.then()チェーンで扱っています。

useThreads.js
import { useEffect, useState } from "react";

export function useThreads() {
  const [threads, setThreads] = useState([]);
  useEffect(() => {
    fetch("/threads")
      .then((response) => response.json())
      .then((data) => {
        setThreads(data);
      });
  }, []);
  return { threads };
}

修正後

async/awaitを使うことで、非同期処理の可読性を向上させました。
データ取得をuseEffectの中で即時実行関数(async () => { ... })();にラップすることで、従来の.then()よりもシンプルに記述できたかと思います。

useThreads.js
import { useEffect, useState } from "react";

export function useThreads() {
  const [threads, setThreads] = useState([]);
  useEffect(() => {
    (async () => {
      const url = "/threads";
      const response = await fetch(url);
      const data = await response.json();
      setThreads(data);
    })();
  }, []);
  return { threads };
}

まとめ

  • 即時関数は関数を定義すると同時に実行できる
  • awaitはトップレベルで使えないので、即時関数を使うと便利
  • .thenはネストが深くなりやすいので、async/awaitを使うと可読性が上がる
  • 即時関数を使うと、関数を定義せずにasync/awaitを活用できる

参考

Discussion