React.js

ReactのAPI GET処理:fetchJsonでJSON取得とエラーを共通化する

この記事でわかること

fetchでJSONを取得する独自関数を作り、HTTPエラー、JSON解析失敗、AbortSignal、取得データの検証を整理します。

fetchでJSONを取得する独自関数を作り、HTTPエラー、JSON解析失敗、AbortSignal、取得データの検証を整理します。

fetchJsonの役割と前提

通信処理をReactから分離する

fetchJson はURLからJSONを取得する自作の非同期関数です。Reactの組み込みAPIではありません。内部でHooksを使わないため、コンポーネント以外からも呼べます。読み込み表示などのstateは呼び出し側が担当します。

fetch はHTTP 404・500でも通常はResponseを返します。成功判定には response.ok が必要です。ネットワークの失敗とHTTPエラー、JSON解析の失敗は異なる段階で発生します。MDNのFetch解説を参考に、扱いを分けます。

src/api/fetchJson.tsの実装

引数と戻り値

url は取得先、任意の signal は中止通知です。戻り値は Promise<unknown> にし、APIの内容を未検証のまま既知の型だと扱わないようにします。

TypeScript
export async function fetchJson(
  url: string,
  signal?: AbortSignal,
): Promise<unknown> {
  const response = await fetch(url, {
    method: 'GET',
    headers: { Accept: 'application/json' },
    signal,
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

JSON以外のHTMLエラーページが200で返る場合も、解析時に失敗します。204の空レスポンスを返すAPIもこの関数の対象外です。JSONを返すエンドポイントに使用し、空レスポンスやファイル取得は別の契約として実装します。

実行できる最小例

public/api/message.jsonを用意する

ViteなどのReact開発環境では public のファイルがルートから配信されます。外部APIのアカウントなしで、GETの動作を練習できます。

JSON
{ "message": "ReactからJSONを読み込みました" }

src/App.tsxで表示する

TSX
import { useState } from 'react';
import { fetchJson } from './api/fetchJson';

export default function App() {
  const [message, setMessage] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  async function handleLoad() {
    setLoading(true);
    setError('');
    setMessage('');
    try {
      const data = await fetchJson('/api/message.json');
      if (typeof data !== 'object' || data === null ||
          !('message' in data) || typeof data.message !== 'string') {
        throw new Error('messageの形式が不正です');
      }
      setMessage(data.message);
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '取得に失敗しました');
    } finally {
      setLoading(false);
    }
  }
  return <main>
    <h1>API読み込み</h1>
    <button type="button" onClick={handleLoad} disabled={loading}>
      {loading ? '読み込み中' : '取得する'}
    </button>
    {error && <p role="alert">{error}</p>}
    <p aria-live="polite">{message}</p>
  </main>;
}

ファイル名を変更して失敗すること、message を数値へ変えて形式エラーになることを確認します。開発サーバーによっては不存在URLへHTMLを200で返すため、404の確認にはネットワークパネルで応答も確認してください。

実際のAPIへ切り替えるとき

CORS・認証・中止を区別する

別オリジンのAPIはサーバー側のCORS許可が必要です。mode: 'no-cors' を付けても読めるJSONにはなりません。ブラウザーへ機密APIキーを置かず、必要なら認証と権限確認を行うサーバーを経由します。

AbortControllersignal を渡せば通信を中止できます。画面の切り替えやURL変更時の競合処理はuseApiDataの記事で実装します。このボタンの例は1回の操作の流れを示すもので、キャッシュや自動再試行を提供しません。

PREFERRED SOURCES

Googleの優先するニュース提供元にSEの部屋を追加

Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。

Googleの設定画面で確認する 新しいタブで開きます

候補にSEの部屋が表示されない場合は、まだ追加できません。

y.
WRITTEN BY

y_ymo10

SEの部屋で、HTML・CSSからJavaScript・React・Next.jsまで、Web制作の開発ノートを公開しています。

ほかの開発ノートを読む →