Day 4・useEffect と データ取得 — 外の世界とつながる
fetchでデータを取ってstateに入れる
useEffectの中でAPIを叩き、返ってきたデータをuseStateに入れて画面に出す。JSで学んだfetchがここで生きる。
データ取得の全体像
外部のAPIからデータを取ってきて画面に出す、というのはアプリで最もよくある処理です。Reactでの流れはいつも同じ形になります。まずuseStateで「取ってきたデータを入れておく箱」を用意します。次にuseEffectの中でfetchを使ってサーバーに問い合わせます。返ってきたデータをsetで箱に入れると、stateが変わったReactが自動で再描画してくれて、画面にデータが現れます。
- useStateで空の箱(初期値)を用意する
- useEffectの中でfetchしてデータを取りに行く
- 取れたデータをsetで箱に入れる
- stateが変わり、Reactが自動で再描画して画面に反映される
なぜfetchはuseEffectの中なのか
コンポーネントの本体で直接fetchすると、再描画のたびに通信が走り、その結果でsetすればまた再描画…と無限ループに陥りがちです。fetchは副作用なので、描画のあとに動くuseEffectに置き、依存配列で「いつ取りに行くか」を制御するのが正しい置き場所です。
.then()で書く基本形
JavaScriptで学んだfetchは、そのままReactでも使えます。fetchはPromiseを返すので、.then()でレスポンスを受け取り、.json()で本文を取り出し、もう一度.then()でsetに渡します。下の例は、記事一覧を初回に一度だけ取ってくる形です。
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>
);
}async/awaitで書きたいとき
async/awaitのほうが読みやすいと感じる人も多いはずです。ただし注意点があります。useEffectに渡す関数そのものをasyncにしてはいけません。async関数はPromiseを返しますが、useEffectは後片付け用の関数が返ってくることを期待しているため、噛み合わなくなるからです。そこで、effectの中に別のasync関数を作って、それをその場で呼び出すのが定番の書き方です。
effectの引数を直接asyncにする(NG)
useEffect(async () => {
const r = await fetch("/api/articles");
const data = await r.json();
setArticles(data);
}, []);
// effectがPromiseを返してしまい、後片付けと噛み合わない中でasync関数を作って呼ぶ
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に噛み砕いてもらいましょう。
理解度チェック
答えられれば、面接で説明できる状態に一歩近づきます。
Reactで取得したデータを画面に反映させる正しい流れは?
useEffectの中でasync/awaitを使いたいとき、正しい書き方は?
なぜfetchはコンポーネント本体ではなくuseEffectの中に書くのか、説明してください。
記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。