R初めてのReact

Day 4useEffect と データ取得 — 外の世界とつながる

fetchでデータを取ってstateに入れる

useEffectの中でAPIを叩き、返ってきたデータをuseStateに入れて画面に出す。JSで学んだfetchがここで生きる。

データ取得の全体像

外部のAPIからデータを取ってきて画面に出す、というのはアプリで最もよくある処理です。Reactでの流れはいつも同じ形になります。まずuseStateで「取ってきたデータを入れておく箱」を用意します。次にuseEffectの中でfetchを使ってサーバーに問い合わせます。返ってきたデータをsetで箱に入れると、stateが変わったReactが自動で再描画してくれて、画面にデータが現れます。

  1. useStateで空の箱(初期値)を用意する
  2. useEffectの中でfetchしてデータを取りに行く
  3. 取れたデータをsetで箱に入れる
  4. stateが変わり、Reactが自動で再描画して画面に反映される

なぜfetchはuseEffectの中なのか

コンポーネントの本体で直接fetchすると、再描画のたびに通信が走り、その結果でsetすればまた再描画…と無限ループに陥りがちです。fetchは副作用なので、描画のあとに動くuseEffectに置き、依存配列で「いつ取りに行くか」を制御するのが正しい置き場所です。

.then()で書く基本形

JavaScriptで学んだfetchは、そのままReactでも使えます。fetchはPromiseを返すので、.then()でレスポンスを受け取り、.json()で本文を取り出し、もう一度.then()でsetに渡します。下の例は、記事一覧を初回に一度だけ取ってくる形です。

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

function ArticleList() {
  const [articles, setArticles] = useState([]);

  useEffect(() => {
    fetch("/api/articles")
      .then((r) => r.json())
      .then((data) => setArticles(data));
  }, []); // 初回に一度だけ取得

  return (
    <ul>
      {articles.map((a) => (
        <li key={a.id}>{a.title}</li>
      ))}
    </ul>
  );
}
.then()チェーンでfetch→json→setとつなぐ

async/awaitで書きたいとき

async/awaitのほうが読みやすいと感じる人も多いはずです。ただし注意点があります。useEffectに渡す関数そのものをasyncにしてはいけません。async関数はPromiseを返しますが、useEffectは後片付け用の関数が返ってくることを期待しているため、噛み合わなくなるからです。そこで、effectの中に別のasync関数を作って、それをその場で呼び出すのが定番の書き方です。

effectの引数を直接asyncにする(NG)

TSX
useEffect(async () => {
  const r = await fetch("/api/articles");
  const data = await r.json();
  setArticles(data);
}, []);
// effectがPromiseを返してしまい、後片付けと噛み合わない

中でasync関数を作って呼ぶ

TSX
useEffect(() => {
  const load = async () => {
    const r = await fetch("/api/articles");
    const data = await r.json();
    setArticles(data);
  };
  load();
}, []);
// effect自体は普通の関数のまま。中でawaitを使える

取得中に画面が変わることを忘れない

fetchは時間がかかるので、返事が来る前に別の画面へ移動したり、依存配列の値が変わって再取得が走ったりします。返事が遅れて届いたとき、すでに消えたコンポーネントにsetしようとすると警告が出ることがあります。まずは「取得は非同期で、あとから返ってくる」という感覚を持っておきましょう。次のレッスンで状態設計を学ぶと、この扱いがぐっと楽になります。

AIにはこう聞く

「このfetchをuseEffectの中でasync/awaitを使って書き直して。effectの引数は直接asyncにしないで、中で関数を作る形にして」と頼むと、定番のパターンで書いてくれます。生成されたコードでも、依存配列とasyncの置き場所は自分で確認しましょう。

読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。

理解度チェック

答えられれば、面接で説明できる状態に一歩近づきます。

Q1

Reactで取得したデータを画面に反映させる正しい流れは?

Q2

useEffectの中でasync/awaitを使いたいとき、正しい書き方は?

Q3

なぜfetchはコンポーネント本体ではなくuseEffectの中に書くのか、説明してください。

記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。