ReactのカスタムフックでAPIの共通処理をまとめる方法!初心者でもわかる効率的な書き方
生徒
「ReactでAPIを使うとき、毎回同じようなコードを書いてしまうんですが、もっと簡単にできませんか?」
先生
「はい、それを解決する方法が『カスタムフック』です。共通処理をひとつにまとめることができます。」
生徒
「共通処理って具体的にはどんなものですか?」
先生
「例えば『データを取得する』『ローディング状態を管理する』『エラーを処理する』など、どのAPIでもよく使う処理です。それを一度作っておけば、どのコンポーネントでも再利用できるんです。」
1. なぜAPI処理を共通化するのか?
ReactでAPIを使うとき、fetch関数やaxiosを使ってデータを取得することが多いです。しかし、そのたびに「ローディング中の表示」や「エラーが出たときの処理」を毎回書くのは効率が悪いです。
共通化とは、こうした「どこでも同じように使う処理」をひとつにまとめることです。例えば料理をするときに「包丁を毎回買う」のではなく「台所にある包丁を何度も使う」ようなイメージです。
2. カスタムフックで共通処理を作る
Reactのカスタムフックを使えば、APIの共通処理をひとつの関数にまとめられます。これによって、同じ処理を何度も書かずに済みます。
import { useState, useEffect } from "react";
function useApi(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
setError(null);
fetch(url, options)
.then((res) => {
if (!res.ok) {
throw new Error("データ取得に失敗しました");
}
return res.json();
})
.then((json) => setData(json))
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
export default useApi;
このカスタムフックでは、data、loading、errorを管理しています。どのコンポーネントからも同じように使えるので便利です。
3. 作ったカスタムフックを使う
それでは実際に作ったカスタムフックを使って、APIからデータを取得してみましょう。
import React from "react";
import useApi from "./useApi";
function App() {
const { data, loading, error } = useApi("https://api.example.com/users");
if (loading) return <p>読み込み中です...</p>;
if (error) return <p>エラーが発生しました: {error}</p>;
return (
<div>
<h1>ユーザー一覧</h1>
<ul>
{data && data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
4. 共通処理をまとめるメリット
APIの共通処理をカスタムフックにまとめると、次のようなメリットがあります。
- コードがシンプルになり読みやすい
- 修正が必要なときに1か所直すだけで全体に反映される
- どのコンポーネントでも同じ使い方ができるので迷わない
これによってReactの開発がスムーズになり、初心者でも扱いやすいコードを書くことができます。
5. 実用的な使い方の例
例えば、認証付きのAPIを呼び出すときに、毎回headersにトークンを入れるのは面倒ですよね。そんなときも、カスタムフックの中で共通処理としてまとめておけば、どこでも同じように使えます。
また、異なるAPIを呼ぶときでも、エラー処理やローディング表示は同じ仕組みをそのまま利用できます。これが「共通化」の大きな強みです。
まとめ
React開発において、APIとの通信は避けて通れない非常に重要な要素です。しかし、プロジェクトが大きくなるにつれて、あちこちのコンポーネントで似たような「データの読み込み待ち(Loading)」や「エラー発生時の処理(Error handling)」、そして「取得データの保持(State management)」のコードが散乱してしまうことがよくあります。これをそのまま放置しておくと、コードの可読性が下がるだけでなく、保守運用が非常に困難になってしまいます。
今回学んだ「カスタムフック」を活用したAPI処理の共通化は、こうした開発現場の悩みを解決する特効薬です。カスタムフックを導入することで、ロジックと見た目(UI)を綺麗に分離できるようになります。これにより、コンポーネント自体は「データをどう表示するか」という本来の役割に集中でき、データ取得の複雑な手順はフックの中に隠蔽(カプセル化)されます。この手法はモダンなフロントエンド開発におけるベストプラクティスのひとつであり、チーム開発でも非常に高く評価されるスキルです。
さらに応用!TypeScript(TSX)での型安全な実装例
実務では、JavaScriptだけでなくTypeScriptを利用して、より安全に開発を行うケースがほとんどです。APIから返ってくるデータの型を定義しておくことで、開発中のタイポを防ぎ、エディタの補完機能を最大限に活用できます。以下に、ジェネリクスを用いた型安全なカスタムフックの例を紹介します。
import { useState, useEffect } from "react";
/**
* 型安全なAPI取得カスタムフック
* T は取得するデータの型を指定するジェネリクスです
*/
function useFetchData<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("ネットワーク応答が正常ではありません");
}
const result = await response.json();
setData(result);
} catch (err) {
if (err instanceof Error) {
setError(err.message);
} else {
setError("予期せぬエラーが発生しました");
}
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetchData;
このように、TypeScriptのジェネリクス(<T>)を使うことで、どのようなデータ構造が返ってきても柔軟に対応できる汎用的なフックが完成します。再利用性がさらに高まり、大規模なアプリケーションでも安心して使い回すことが可能になります。
API共通化がもたらす開発効率の向上
一度カスタムフックを構築してしまえば、新しい画面を作る際にやるべきことは「URLを渡してフックを呼び出すだけ」になります。これまでは何十行も書いていたAPI通信コードが、たった一行で完結する快感は、React開発の醍醐味とも言えるでしょう。
また、将来的に通信ライブラリを標準の fetch から axios に変更したくなったり、エラー時のログ出力を全画面共通で追加したくなったりした場合でも、このカスタムフックの中身を修正するだけで、プロジェクト全体の挙動を一瞬で変更できます。これは「DRY(Don't Repeat Yourself:同じことを繰り返さない)」というプログラミングの鉄則に基づいた、非常に理にかなった設計です。
これからのReact開発では、単に動くものを作るだけでなく、「いかに効率よく、メンテナンスしやすいコードを書くか」を意識することが脱・初心者への近道です。ぜひ今回のカスタムフックを自身のプロジェクトに取り入れて、その便利さを体感してみてください。
生徒
「先生、カスタムフックって最初は難しそうに感じましたが、使ってみると驚くほどコードがスッキリしますね!今までコンポーネントの中が useEffect や useState でごちゃごちゃしていたのが嘘みたいです。」
先生
「そう感じてもらえて嬉しいです。特にAPI通信のような『決まりきった手順』が多い処理ほど、カスタムフック化するメリットは大きいんですよ。コードの見た目が綺麗になるだけでなく、バグが入り込む余地も減らせますからね。」
生徒
「たしかに!もしエラーハンドリングの方法を変えたいと思っても、フックのファイルを一箇所直すだけで、アプリ全体の挙動が変わるというのは本当に便利だと思います。TypeScript版の書き方も、型がしっかり決まっていて安心感がありますね。」
先生
「その通りです。実際の現場では、さらにキャッシュ機能を追加したり、リトライ処理(失敗した時にもう一度試す処理)を組み込んだりと、フックをどんどん進化させていくことも多いですよ。まずは基本の形をしっかりマスターして、自分の使いやすいようにカスタマイズしていってくださいね。」
生徒
「はい!自分でフックの名前を useMyApi とか useUserData みたいに自由に決められるのも愛着が湧きますね。これから作る機能では、積極的にロジックを切り出して、美しいコンポーネント作りを目指します!」
先生
「その意気です!美しいコードは自分だけでなく、一緒に働く仲間も幸せにします。これからも一歩ずつ、モダンなReact開発を学んでいきましょう。応援していますよ!」