react + typescriptでapiを叩くとき最近だと何が使われているのかなと気になり調査したところfetch + TanStack Queryが流行っているようなので、主流だったであろうaxiosとの対比も踏まえて記事に書いていこうと思います。
TanStack Queryとは
TanStack Query(旧 React Query)は、Reactなどのフロントエンドでサーバーから取得したデータの取得・キャッシュ・同期を簡単に管理するためのライブラリです。
通常、APIからデータを取得するときは fetchやaxiosといったHTTPクライアントを使います。
ただ、データ取得だけでは、以下のような課題が出てきます:
- 同じデータを複数コンポーネントで使いたいときに状態を共有するのが面倒
- ローディング中・エラー状態を毎回自分で管理する必要がある
- データが変わったときに自動で再取得したい
- キャッシュして無駄なAPI呼び出しを減らしたい
TanStack Queryは、これらの課題をライブラリ側で自動管理してくれる点が特徴です。
具体的には、以下のような機能があります:
- useQuery / useMutation などのフックを提供し、データ取得・更新を簡単に書ける
- 取得済みデータを自動でキャッシュし、再利用可能
- 複数コンポーネントで同じqueryKeyを使うとキャッシュが共有される - ローディング・エラー・フェッチ中の状態を自動で管理
- useQuery を呼ぶと、返り値として以下の状態情報が自動で含まれる
- data … 取得済みのデータ(まだ取得中なら undefined)
- isLoading … データがまだ取得されていない状態かどうか
- isError … エラーが発生しているか
- error … 発生したエラーオブジェクト
- isFetching … 再フェッチ中かどうか(既にキャッシュがある場合も true になる) - データのバックグラウンド更新や再取得を自動で行える
実装例
api.tsの準備
fetchライブラリでデータを取得する処理
// src/lib/api.ts
export async function api<T>(
path: string,
options?: RequestInit
): Promise<T> {
const res = await fetch(
import.meta.env.VITE_API_URL + path,
{
credentials: "include",
headers: {
"Content-Type": "application/json",
},
...options,
}
)
if (!res.ok) {
const text = await res.text()
throw new Error(text || res.statusText)
}
return res.json()
}
TanStack Query セットアップ
$ npm install @tanstack/react-query
// src/main.tsx
import React from "react"
import ReactDOM from "react-dom/client"
import { Router } from "./router"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
const queryClient = new QueryClient()
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
</React.StrictMode>
)
APIごとのReact Hookを作成
// src/features/user/useMe.ts
import { useQuery } from "@tanstack/react-query"
import { api } from "@/lib/api"
export type User = {
id: string
email: string
name: string | null
}
export function useMe() {
return useQuery({
queryKey: ["me"],
queryFn: () => api<User>("/api/users/me"),
})
}
ポイント
- queryKeyはキャッシュの識別子
- queryFnが実際にデータを取得する関数
- TanStack Queryが自動でローディング・エラー・データを管理
Reactコンポーネントで使う
// src/components/Profile.tsx
import { useMe } from "@/features/user/useMe"
export function Profile() {
const { data, isLoading, error } = useMe()
if (isLoading) return <p>Loading...</p>
if (error) return <p>Not logged in</p>
return (
<div>
<p>ID: {data.id}</p>
<p>Email: {data.email}</p>
<p>Name: {data.name}</p>
</div>
)
}
ポイント
- isLoading / error / data は TanStack Queryが自動で提供
- 複数コンポーネントで同じqueryKeyを使うとキャッシュが共有される
注意
CORSエラーが発生することがあるため、バックエンド側にCORS設定を入れること
Bun×Honoの例
const FRONTEND_URL =
process.env.FRONTEND_URL ?? "http://localhost:5173"
app.use(
"*",
cors({
origin: FRONTEND_URL,
credentials: true,
})
)