TypeScriptでAPI通信を共通化!Reactで使える再利用可能なカスタムHooksの作り方完全ガイド(Fetch・Axios対応)
生徒
「ReactとTypeScriptを使って、インターネット上のデータを読み込むプログラムを作っています。でも、毎回同じような通信プログラムを書くのが大変で、画面が増えるたびにコードが長くなってしまいます。もっと綺麗に整理して、何度も使い回せるようにする方法はありますか?」
先生
「そんなときこそ、TypeScriptを使って再利用可能なカスタムHooksという仕組みを作るのが一番の解決策です。これを使えば、面倒な通信処理を一つの部品としてまとめることができ、色々な画面で簡単に使い回せるようになりますよ。」
生徒
「カスタムHooksって難しそうですね。パソコン初心者でも理解して作れるようになりますか?」
先生
「大丈夫です。通信の仕組みや言葉の意味から、一つずつ順番に、とても分かりやすく解説していきます。それでは、基本的な使い方と作り方を一緒に見ていきましょう!」
1. API通信とカスタムHooksの基本知識
インターネットの世界では、画面に表示するデータをどこか別の場所から貰ってくることがよくあります。このデータを貰うための窓口のことをAPIと呼びます。そして、その窓口に対してデータをくださいとお願いする行動のことをAPI通信と言います。身近な例で言うと、お店の受付カウンターで商品を注文して、裏の倉庫から持ってきてもらうようなイメージです。このとき、注文する役割を持つのがパソコンの画面で、倉庫の役割をするのがサーバーと呼ばれる大きなコンピューターです。
画面を作る仕組みであるReactでは、この通信を行うときに少し特別な書き方をします。しかし、色々な画面で毎回その長い書き方をしていると、同じプログラムが何度も登場してしまい、間違いの原因になります。そこで登場するのがカスタムHooksという仕組みです。カスタムHooksとは、よく使うお決まりの処理を一つの便利な道具箱のようにまとめて、どこからでも簡単に呼び出せるようにした自作の仕組みのことです。これに型というルールを決めるTypeScriptを組み合わせることで、安全で使いやすい強力な道具を作ることができます。
2. 通信でよく使うFetchとAxiosの違いとは?
API通信を行うためには、専用の道具を使う必要があります。代表的な道具として、FetchとAxiosの二つがあります。これらはどちらもデータを取ってくるための道具ですが、少し特徴が違います。初めてパソコンを触る方に向けて、乗り物に例えて説明します。
Fetchは、パソコンをはじめから動かすシステムに標準で備わっている自転車のようなものです。新しく何かを準備する必要がなく、いつでもすぐに乗り始めることができます。シンプルな通信であればこれだけで十分対応できます。一方で、Axiosは追加で用意する高機能な自動車のようなものです。長距離の移動や、たくさんの荷物を運ぶときに便利な機能が最初からたくさん付いています。例えば、通信が途中で失敗したときに自動でもう一度やり直す機能や、データを扱いやすい形に自動で変換してくれる機能があります。どちらを使っても目的は達成できますが、今回はこの二つの道具を使ったそれぞれの書き方を解説します。
3. データを準備するための型定義について学ぼう
TypeScriptを使う最大のメリットは、これから扱うデータがどのような形をしているかを事前に紙に書いて予約できることです。この予約のことを型定義と呼びます。例えば、お買い物の履歴データを扱うときに、そこには商品名という文字が入るのか、それとも金額という数字が入るのかをあらかじめ明確にしておくことで、間違ったデータを入れようとしたときにパソコンが自動で注意してくれます。これにより、プログラムが動かなくなる失敗を未然に防ぐことができます。
今回は、インターネットからユーザーの情報を取得する場面を考えてみましょう。ユーザーには識別するための番号、名前、そしてメールアドレスがあるとします。これをTypeScriptの言葉で表現すると、以下のようになります。まずは一番基本となるデータの設計図を作ってみましょう。
type UserData = {
id: number;
name: string;
email: string;
};
このプログラムでは、UserDataという名前のデータの型を定義しています。idには数字が入ることを示すnumberを指定し、nameとemailには文字が入ることを示すstringを指定しています。これで、データの形が綺麗に整いました。この設計図を元にして、次のステップで通信の道具を作っていきます。
4. Fetchを使った再利用可能なカスタムHooksの作成
それでは、実際に使い回しができる通信の道具を作っていきましょう。Reactでは自作の道具を作るとき、名前の先頭にuseをつけるという決まりがあります。今回はuseFetchという名前の関数を作ります。この中では、通信中であることを表す状態、データが正しく取れた時の状態、そして失敗したときのエラーの状態の三つを管理します。これらをまとめて一つの道具として外に出すことで、どの画面からでも一行で通信ができるようになります。
以下のコードは、標準のFetch機能を使って、指定された場所からデータを取ってくるためのカスタムHooksのプログラムです。初心者の方でも分かりやすいように、中身を細かく分けて記述しています。
import { useState, useEffect } from "react";
export function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error("データの取得に失敗しました");
}
return response.json();
})
.then((actualData) => {
setData(actualData);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
ここで使われている記号について説明します。関数の後ろにある角括弧のような記号は、ジェネリクスと呼ばれる仕組みです。これは、使うときまでデータの種類を確定させず、後からどんな型のデータでも当てはめることができるようにする魔法の箱です。これにより、ユーザー情報だけでなく、商品の情報やニュースの記事など、どんなデータにも対応できる本当の意味での再利用可能な道具になります。
5. 作成したFetch用Hooksを実際の画面で使う方法
道具が完成したので、次はそれをスマートに画面に表示させてみましょう。先ほど作ったuseFetchを呼び出すだけで、画面が読み込まれた瞬間に自動でインターネットへの通信が始まり、結果が表示されます。これによって、画面を作るためのプログラムが驚くほど短く、すっきりと読みやすくなります。
以下のプログラムは、先ほど定義したユーザー情報の型と、自作したuseFetchを組み合わせて、実際に画面へ名前を表示する仕組みです。通信の状態に合わせて、画面の表示を切り替える工夫も入れています。
import React from "react";
import { useFetch } from "./useFetch";
type User = {
id: number;
name: string;
email: string;
};
export function UserProfileComponent() {
const { data, loading, error } = useFetch<User>("https://api.example.com/user/1");
if (loading) {
return <p>現在データを読み込んでいます。少々お待ちください。</p>;
}
if (error) {
return <p>エラーが発生しました。理由:{error}</p>;
}
return (
<div>
<h3>会員情報</h3>
<p>お名前:{data?.name}</p>
<p>連絡先:{data?.email}</p>
</div>
);
}
この画面プログラムの中には、面倒な通信の命令が一切書かれていません。ただ道具を呼び出して、中身を取り出しているだけです。これがカスタムHooksの最大の魅力です。もし通信がまだ終わっていなければ読み込み中のメッセージを出し、失敗すればエラーを表示し、成功すれば名前を出すという、条件に応じた切り替えも簡単に行うことができます。
6. Axiosを使ったより高度なカスタムHooksの作成
次に、もう一つの人気な道具であるAxiosを使った場合のカスタムHooksを作ってみましょう。Axiosを使う場合は、通信の命令部分が少しシンプルになります。なぜなら、受け取ったデータを文字からオブジェクトというパソコンが理解しやすい形へ自動で翻訳してくれるからです。また、エラーが起きたときも詳細な情報を自動でまとめてくれるため、大きなシステムを作るときによく好まれます。
以下のプログラムは、Axiosの道具を組み込んで作ったuseAxiosというカスタムHooksの例です。基本的な動きの流れはFetchのときと同じですが、より安定した通信を行うための書き方になっています。
import { useState, useEffect } from "react";
import axios from "axios";
export function useAxios<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setLoading(true);
axios.get<T>(url)
.then((response) => {
setData(response.data);
setLoading(false);
})
.catch((err) => {
setError(err.message || "予期せぬエラーが起きました");
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
このコードの中のaxios.getの部分に注目してください。ここでも先ほど登場したジェネリクスが使われており、取得するデータの型をそのまま引き継ぐことができるようになっています。これにより、Axiosを使ってもTypeScriptの安全性をしっかりと保ちながら開発を進めることができます。
7. 通信エラーが発生したときの親切な対処法
インターネットの通信は、いつでも必ず成功するとは限りません。スマートフォンの電波が悪くなったり、データを管理しているサーバーが一時的に眠ってしまったりすることがあります。そのため、プログラミングをするときは、失敗したときのことを最初から考えておくことがとても大切です。これを例外処理やエラーハンドリングと呼びます。
もし失敗したときのプログラムを書き忘れてしまうと、画面が真っ白になってしまい、操作している人がどうしていいか分からなくなってしまいます。私たちの作ったカスタムHooksでは、エラーの内容を文字として保存して画面に渡す仕組みにしています。これにより、ただ失敗したと伝えるだけでなく、何が原因でダメだったのかを画面に優しく表示させることが可能になります。システム開発において、利用者に不安を与えない仕組みづくりは非常に重要な要素です。
8. 実行結果を確認してプログラムの動きを理解しよう
実際に作成したカスタムHooksが動いたとき、パソコンの内部でどのようなデータがやり取りされているかを確認してみましょう。プログラムが正しくデータを読み込むと、以下のような形式のデータが完成して画面に届きます。これをJSON形式と呼び、インターネットでデータを送受信するときの世界共通の書き方です。
{
"data": {
"id": 1,
"name": "山田太郎",
"email": "yamada@example.com"
},
"loading": false,
"error": null
}
この結果を見ると、loadingがfalseになっており、読み込みが既に完了していることが分かります。そしてerrorがnull、つまり何もない空っぽの状態を意味しているので、通信が大成功したことを表しています。このように、データがどのような状態に変化したかを意識しながら組み立てていくことで、プログラムの動きをパズルのように楽しく理解できるようになります。これで、複数の画面で何度でも使い回せる最強のAPI通信 Hooksの完成です。