React Native Course
Data Fetching

fetch と useEffect でデータを取得し、FlatList で一覧表示する。行の文言から data と renderItem を辿る第5話です。

API とリスト — 取得したデータを画面に並べる

これまでの記事では、画面に出ていた文言はコード内に直書きされていました。第2話 のカウンターも、第4話 の入力欄も、値はアプリ内の state だけで完結していました。

実アプリでは、サーバーから取ったデータを一覧表示することが多いです。「この行のタイトルはどこから来ているのか」と感じたときも、読み方の軸は同じです。見えている文言から逆算する。第5話では fetchuseEffect で投稿一覧を取得し、FlatList で並べながら、1 行のタイトル → 配列 → 取得処理を辿る練習をします。

この記事で扱うこと

実装(A)読み解き(B)
データの形JSONPlaceholder のレスポンス確認id / title / body の対応
取得fetch + useEffect、loading / errorエラー文言 → catch / 条件分岐
一覧FlatList + renderItem行の文言 → itemdata 配列
第4話との接続GET で一覧を受け取るPOST 送信との役割の違い
まとめ次に進む順番第6話(シリーズ最終話)への導線

前提: 第2話useState)。第4話のフォームは任意です。

1. データの形を先に確認する

本記事では、認証不要の公開 API JSONPlaceholder を使います。投稿一覧の URL は次のとおりです。

https://jsonplaceholder.typicode.com/posts

ブラウザで開くか、ターミナルで curl すると、JSON の配列が返ります。先頭の 1 件はおおよそ次の形です。

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit..."
}

本記事では idtitlebody だけ使います。TypeScript では次のように型を定義します。

type Post = {
  id: number;
  title: string;
  body: string;
};

一覧の見た目を読み解くときは、画面上に見えている title の文字列が、この JSON の title フィールドに対応している、と押さえておけば十分です。

2. fetch と useEffect — 取得の最小例

第2話と同様、1 画面・1 ファイルに絞ります。エントリファイルを次の内容に差し替えてください。

import { useEffect, useState } from "react";
import {
  ActivityIndicator,
  FlatList,
  StyleSheet,
  Text,
  View,
} from "react-native";

type Post = { id: number; title: string; body: string };

export default function PostListScreen() {
  const [posts, setPosts] = useState<Post[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function loadPosts() {
      try {
        const res = await fetch(
          "https://jsonplaceholder.typicode.com/posts?_limit=10"
        );
        if (!res.ok) throw new Error("データの取得に失敗しました");
        const data: Post[] = await res.json();
        setPosts(data);
      } catch (e) {
        setError(e instanceof Error ? e.message : "不明なエラー");
      } finally {
        setLoading(false);
      }
    }
    loadPosts();
  }, []);

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator />
        <Text>読み込み中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.error}>{error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={posts}
      keyExtractor={(item) => String(item.id)}
      renderItem={({ item }) => (
        <View style={styles.row}>
          <Text style={styles.title}>{item.title}</Text>
        </View>
      )}
      contentContainerStyle={styles.list}
    />
  );
}

const styles = StyleSheet.create({
  center: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    gap: 8,
  },
  error: {
    color: "#dc2626",
  },
  list: {
    padding: 16,
    gap: 12,
  },
  row: {
    paddingVertical: 12,
    borderBottomWidth: 1,
    borderColor: "#e2e8f0",
  },
  title: {
    fontSize: 16,
    fontWeight: "600",
  },
});

bunx expo start で起動すると、まず「読み込み中…」が出て、続けて投稿タイトルの一覧が表示されます。

ポイントは次の 4 つです。

  • useEffect(..., []) は画面が表示されたときに 1 回だけ fetch する
  • posts は第2話の count と同様 state。ただし型は 配列Post[]
  • 表示は loading / error / 成功 の 3 分岐に分ける
  • 成功時は FlatListdata={posts}renderItem で各行を描画する
画面の状態条件見えるもの
読み込み中loading === trueスピナーと「読み込み中…」
エラーerror に文字列がある赤いエラーメッセージ
成功上記以外投稿タイトルのリスト

3. 読み解きの練習 — 行のタイトルから取得処理まで

第1話以降と同じ型で、いまリストに見えている文言から入ります。

  1. 一覧の投稿タイトルを 1 つ選び、エディタで全文検索する(英語の長いタイトルでも、一部の単語だけでも可)
  2. renderItem 内の {item.title} であることを確認する
  3. FlatListdata={posts} から posts state を辿る
  4. setPosts(data) を探し、fetchres.json() まで遡る
見ているもの辿り先
行のタイトル文字列renderItemitem.title
行の数・順番data={posts} の配列
「読み込み中…」loading state と if (loading)
「データの取得に失敗しました」error state と catch

読み解きのキーワードは次のとおりです。

行の文言 → renderItem → data → posts → fetch → useEffect

2 行目以降も同じ renderItem パターンで描画されています。行の見た目(余白・区切り線)を変えたいときは styles.rowstyles.title を辿ります。

4. FlatList の要点

短いリストなら ScrollViewmap でも表示できます。件数が増えると、画面外の行まで一度に描画しようとして重くなりやすいです。FlatList見えている行付近だけ を効率よく描画するため、一覧画面でよく使われます。

keyExtractor={(item) => String(item.id)} は、各行を React が識別するためのキーです。id がユニークなら、リストの更新時に警告が出にくくなります。

React Query や SWR、ページネーション、pull-to-refresh は本記事のスコープ外です。

5. つまずきやすい点

症状原因の目安
ずっと「読み込み中…」setLoading(false)finally にない、fetch が完了していない
真っ白なリストdata が空、または renderItem が何も返していない
同じキー警告keyExtractor がない、または id が重複している
実機で取得できないオフラインなど。JSONPlaceholder は HTTPS なので、通常はネット接続があれば動く

オフラインのときは error 分岐に入り、エラーメッセージが表示されます。その文言から catch まで辿るのも、読み解きの練習になります。

6. 第4話との接続

第4話のフォームでは、名前を入力して「送信」ボタンで console.log しました。実アプリでは、その送信が POST でサーバーに届き、別の画面で GET して一覧表示する、という流れがよくあります。

本記事は GET で一覧を受け取る 側だけを扱います。第4話の handleSubmit の中身を、将来 fetch の POST に差し替えるイメージでつながります。用語だけ整理すると、送信は POST、一覧は GET です。

7. まとめ

実装の順番

  1. useEffect でマウント時に fetch する
  2. loading / error / 成功の 3 状態を分ける
  3. FlatListdatarenderItem で行を描画する

読み解きの順番

  1. リスト行の文言を検索する
  2. item.xxxdata 配列を辿る
  3. setPosts から fetch まで遡る

ここまでできれば、API 由来の一覧画面を読める土台ができています。第6話(シリーズ最終話)では、保存・取得処理を含めた 読み解きの総復習 を行います。