Next.jsで学ぶReact講座(完全初心者向け・30日)演習問題・課題 | 第4章:実用的React – データ取得

React Next.js
スポンサーリンク

この課題のねらい

ここはいよいよ「実用的 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();
}, []);
TSX

JSXで配列データを表示する

あとは、postsmap で回して画面に出します。

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

ここでやっていることはシンプルです。

  • postsnull でなければ(データが入っていれば)リストを表示。
  • posts.map(...) で、1件ずつ <li> に変換。
  • key={post.id} で一意な ID を指定。

これで、「API から取ってきたデータを画面に表示する」という基本の流れが完成します。


挑戦課題 取得失敗時の表示

エラーとローディング状態をstateで持つ

実用的なアプリでは、「成功したとき」だけでなく「読み込み中」「失敗したとき」もちゃんと扱う必要があります。
そのために、次の 2 つの state を追加します。

const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
TSX
  • isLoading: データ取得中かどうか。
  • 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 で画面に表示する。
  • isLoadingerror の state を用意して、「読み込み中」「失敗」「成功」で表示をきちんと切り替える。

ここまでできれば、

  • 記事一覧
  • ユーザー一覧
  • TODO の取得
  • 外部サービスからのデータ表示

など、「API とつながる React アプリ」の基本形を自分で組めるようになります。

タイトルとURLをコピーしました