概要
-
React
コンポーネントで外部APIを呼び出す方法として、再利用可能なuseFetch
カスタムフックを活用する方法を紹介 - 最小構成から、実誤的なAPI活用例「JSONPlaceholder / GitHub API / CoinMarketCap API」までを解説
-
TypeScript
による汎用的な型定義で、型安全かつ保守性の高い構成を実現
実施条件
- React + TypeScript プロジェクトが構築済みであること
- 外部APIにアクセスできるネットワーク環境があること
- npm/yarn で依存関係を追加できる環境であること
- CoinMarketCapのAPIキー〈無料で取得可能〉を所持していること
環境
ツール | バージョン | 目的 |
---|---|---|
Node.js | 22.5.1 | Reactアプリ実行環境 |
React | 19.1.0 | UIコンポーネントの構築 |
TypeScript | 4.9 | 型定義による安全な開発 |
fetch API | ブラウザ標準搭載 | API通信処理 |
型定義セクション
// 型定義セクション
export type MessageResponse = {
message: string;
};
export type Post = {
userId: number;
id: number;
title: string;
body: string;
};
export type GitHubUser = {
login: string;
avatar_url: string;
html_url: string;
};
export type CoinMarketData = {
data: {
[symbol: string]: {
name: string;
quote: {
USD: {
price: number;
};
};
};
};
};
カスタムフックにおける基本構造
- importセクション
- 型定義セクション
- カスタムフック定義セクション
- 内部状態管理セクション
- 副作用(useEffect)処理セクション
- 返り値構築・ロジックセクション
基本構文: useFetch.ts
// importセクション
import { useEffect, useState } from 'react';
// カスタムフック定義セクション
export const useFetch = <T>(url: string, options?: RequestInit) => {
// 内部状態管理セクション
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);
// 副作用(useEffect)処理セクション
useEffect(() => {
fetch(url, options)
.then((res) => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
return res.json();
})
.then((json: T) => setData(json))
.catch((err: Error) => setError(err.message))
.finally(() => setLoading(false));
}, [url, JSON.stringify(options)]);
// 返り値構築・ロジックセクション
return { data, error, loading };
}
活用例
1. 投稿のタイトルを取得する(JSONPlaceholder)
// importセクション
import React from 'react';
import { useFetch } from './useFetch';
// 型定義セクション
import type { Post } from './types';
// 関数定義セクション
const FetchPostTitleWithHook: React.FC = () => {
// 内部状態管理セクション(useFetchの呼び出し)
const { data, error, loading } = useFetch<Post>('https://jsonplaceholder.typicode.com/posts/1');
// イベントハンドラーセクション(特になし)
// JSX構築セクション
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラー: {error}</p>;
if (!data) return <p>データなし</p>;
return <h1>{data.title}</h1>;
};
export default FetchPostTitleWithHook;
2. GitHubユーザーのプロフィールを表示する
// importセクション
import React from 'react';
import { useFetch } from './useFetch';
// 型定義セクション
import type { GitHubUser } from './types';
// 関数定義セクション
const GitHubProfileWithHook: React.FC = () => {
// 内部状態管理セクション(useFetchの呼び出し)
const { data: user, error, loading } = useFetch<GitHubUser>('https://api.github.com/users/octocat');
// イベントハンドラーセクション(特になし)
// JSX構築セクション
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラー: {error}</p>;
if (!user) return <p>データなし</p>;
return (
<div>
<h2>{user.login}</h2>
<img src={user.avatar_url} alt="avatar" width={100} />
<a href={user.html_url} target="_blank" rel="noopener noreferrer">GitHubを見る</a>
</div>
);
};
export default GitHubProfileWithHook;
3. CoinMarketCap APIでビットコインの価格を取得する
// importセクション
import React from 'react';
import { useFetch } from './useFetch';
// 型定義セクション
import type { CoinMarketData } from './types';
// 関数定義セクション
const CoinPriceWithHook: React.FC = () => {
// 内部状態管理セクション(useFetchの呼び出し)
const { data, error, loading } = useFetch<CoinMarketData>(
'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=BTC'
);
// イベントハンドラーセクション(特になし)
// JSX構築セクション
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラー: {error}</p>;
if (!data?.data?.BTC) return <p>データなし</p>;
const price = data.data.BTC.quote.USD.price;
return <h2>Bitcoin価格: ${price.toFixed(2)}</h2>;
};
export default CoinPriceWithHook;
fetchプロセスの流れ
-
useEffect
により初回レンダリング時にAPI呼び出し -
fetch
でHTTPリクエストを送信 -
.json()
でJSON解析 -
setState
で状態更新し、UI再描画