コンテンツにスキップ

データフェッチ

外界と通信した結果を画面表示する場合を考える。

useEffect()を使う

原始的なやり方である。通信の処理を書いたり読込中の状態を考慮したりでごちゃごちゃしやすい。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { useEffect, useState } from "react";

export interface Data {
  message: string;
}

export async function fetchData(): Promise<Data> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ message: "Hi" });
    }, 3000);
  });
}

function App() {
  const [data, setData] = useState<Data | undefined>(undefined);

  useEffect(() => {
    fetchData().then((value) => {
      setData(value);
    });
  }, []);

  if (!data) {
    return <p>Loading...</p>;
  }

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

export default App;

Suspense と use を使う

use – React

クライアントサイドで通信した結果を表示する場合、React 19 以降は Suspense と use を使うのが良いらしい。

use は Promise を解決してくれる。

なお Next.js などサーバーサイドで通信した結果を表示する場合は React Server Components (RSC)を使う。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Suspense, use } from "react";

export interface Data {
  message: string;
}

export async function fetchData(): Promise<Data> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ message: "Hi" });
    }, 3000);
  });
}

function DataComponent(props: { dataPromise: Promise<Data> }) {
  const data = use(props.dataPromise);

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

function App() {
  const dataPromise = fetchData();

  return (
    <Suspense fallback={<p>Loading...</p>}>
      <DataComponent dataPromise={dataPromise} />
    </Suspense>
  );
}

export default App;

Promise でエラーが起きたときの表示は ErrorBoundary を使う。

1
$ npm i react-error-boundary
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { Suspense, use } from "react";
import { ErrorBoundary } from "react-error-boundary";

export interface Data {
  message: string;
}

export function fetchData(): Promise<Data> {
  return new Promise((resolve, reject) => {
    // throw new Error("Something happened");
    setTimeout(() => {
      reject("Something happened");
      resolve({ message: "Hi" });
    }, 3000);
  });
}

function DataComponent(props: { dataPromise: Promise<Data> }) {
  const data = use(props.dataPromise);

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

function App() {
  const dataPromise = fetchData();

  return (
    <ErrorBoundary fallback={<p>Error</p>}>
      <Suspense fallback={<p>Loading...</p>}>
        <DataComponent dataPromise={dataPromise} />
      </Suspense>
    </ErrorBoundary>
  );
}

export default App;

setTimeout 内での throw は補足できないので reject を使うこと。