この課題のねらい
ここはいよいよ「実用的 React」のど真ん中です。
テーマは「API からデータを取ってきて、画面に表示する」という超・現場的な流れ。
fetch でデータを取得する
取得したデータを state に入れて画面に表示する
失敗したときはエラーメッセージを出す
この3つを一連の流れとして体で覚えると、「ただのカウンター」から一気に“アプリ”に変わります。
基本パターンの全体像
まずは完成イメージから
最初に、よくある「データ取得コンポーネント」の形をざっくり見ておきます。
"use client";
import { useEffect, useState } from "react";
type Post = {
id: number;
title: string;
};
export default function FetchExample() {
const [posts, setPosts] = useState<Post[] | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchPosts() {
try {
setIsLoading(true);
setError(null);
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
if (!res.ok) {
throw new Error(`HTTPエラー: ${res.status}`);
}
const data: Post[] = await res.json();
setPosts(data);
} catch (err) {
console.error(err);
setError("データの取得に失敗しました。時間をおいて再度お試しください。");
} finally {
setIsLoading(false);
}
}
fetchPosts();
}, []);
if (isLoading) {
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<p>読み込み中です...</p>
</main>
);
}
if (error) {
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<p style={{ color: "red" }}>{error}</p>
</main>
);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>記事一覧</h1>
{posts && (
<ul style={{ marginTop: "12px" }}>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)}
</main>
);
}
TSXこの中に、今回の必須課題と挑戦課題の要素が全部入っています。
ここから分解して、ひとつずつ噛み砕いていきます。
必須課題① fetchでAPI取得
useEffectの中で非同期処理を書く
React コンポーネントの中で API を叩くときは、基本的に useEffect の中でやります。
理由は、「レンダリングのたびに毎回 fetch されるのを防ぐため」です。
初回表示時に一度だけデータを取りたいので、依存配列は [] にします。
useEffect(() => {
async function fetchPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const data = await res.json();
console.log(data);
}
fetchPosts();
}, []);
TSXポイントはここです。
useEffectの中でasync関数を定義して、その中でawait fetch(...)を使う。- 依存配列
[]によって、「初回表示時に一度だけ」実行される。
まずは console.log(data) で、ブラウザのコンソールにデータが出ているか確認してみてください。
「まずログで中身を確認する」は、実務でもめちゃくちゃ大事な習慣です。
必須課題② データをstateに入れて画面表示
取得したデータをstateに保存する
次のステップは、「ログで見えたデータを、画面にちゃんと表示する」です。
そのために、useState でデータ用の state を用意します。
type Post = {
id: number;
title: string;
};
const [posts, setPosts] = useState<Post[] | null>(null);
TSXそして、fetch したデータを setPosts で保存します。
useEffect(() => {
async function fetchPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const data: Post[] = await res.json();
setPosts(data);
}
fetchPosts();
}, []);
TSXJSXで配列データを表示する
あとは、posts を map で回して画面に出します。
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>記事一覧</h1>
{posts && (
<ul style={{ marginTop: "12px" }}>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)}
</main>
);
TSXここでやっていることはシンプルです。
postsがnullでなければ(データが入っていれば)リストを表示。posts.map(...)で、1件ずつ<li>に変換。key={post.id}で一意な ID を指定。
これで、「API から取ってきたデータを画面に表示する」という基本の流れが完成します。
挑戦課題 取得失敗時の表示
エラーとローディング状態をstateで持つ
実用的なアプリでは、「成功したとき」だけでなく「読み込み中」「失敗したとき」もちゃんと扱う必要があります。
そのために、次の 2 つの state を追加します。
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
TSXisLoading: データ取得中かどうか。error: エラーメッセージ(なければ null)。
fetchの中で成功・失敗・終了を分けて書く
useEffect の中の処理を、少し丁寧に書き直します。
useEffect(() => {
async function fetchPosts() {
try {
setIsLoading(true);
setError(null);
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
if (!res.ok) {
throw new Error(`HTTPエラー: ${res.status}`);
}
const data: Post[] = await res.json();
setPosts(data);
} catch (err) {
console.error(err);
setError("データの取得に失敗しました。時間をおいて再度お試しください。");
} finally {
setIsLoading(false);
}
}
fetchPosts();
}, []);
TSXここでの流れを整理します。
tryブロック- ローディング開始 →
setIsLoading(true) - エラーを一旦リセット →
setError(null) fetchでデータ取得res.okをチェックして、ダメならthrowでエラー扱いsetPosts(data)でデータ保存
- ローディング開始 →
catchブロック- 何かエラーが起きたらここに来る
console.errorで詳細をログに出す- ユーザー向けのメッセージを
setErrorでセット
finallyブロック- 成功しても失敗しても最後に必ず実行される
setIsLoading(false)で「読み込み中ではない」状態にする
ローディング中・エラー時・成功時で表示を切り替える
最後に、JSX 側で状態に応じて表示を分岐させます。
if (isLoading) {
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<p>読み込み中です...</p>
</main>
);
}
if (error) {
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<p style={{ color: "red" }}>{error}</p>
</main>
);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>記事一覧</h1>
{posts && (
<ul style={{ marginTop: "12px" }}>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)}
</main>
);
TSXこの 3 段階がとても大事です。
isLoading === trueの間は「読み込み中…」だけ表示。error !== nullならエラーメッセージを表示。- どちらでもないとき(成功時)にだけ、データのリストを表示。
これができると、「ちゃんとしたアプリ」の顔になります。
まとめ:データ取得でつかんでほしいこと
この課題で本当に押さえてほしいのは、次の流れです。
useEffectの中でfetchを使い、初回表示時に一度だけ API を叩く。- 取得したデータを
useStateに保存し、mapで画面に表示する。 isLoadingとerrorの state を用意して、「読み込み中」「失敗」「成功」で表示をきちんと切り替える。
ここまでできれば、
- 記事一覧
- ユーザー一覧
- TODO の取得
- 外部サービスからのデータ表示
など、「API とつながる React アプリ」の基本形を自分で組めるようになります。
