先に要点
- TanStack Query(旧 React Query)は、`サーバから取ったデータ(server state)』 を扱う専用ライブラリ。`useState + useEffect + fetch + ローディング + エラー + 再取得』 を毎回書く苦痛を ` useQuery』 1行 で解消する。
- 核は ` stale-while-revalidate』 のキャッシュ戦略。`古いキャッシュをまず表示 → バックグラウンドで新しいデータを取り直す → 変わっていれば更新』 という挙動を、デフォルトで提供する。
- 提供機能は広い: ` キャッシュ管理 / 再試行 / 自動再取得(focus / online)/ ページネーション / 無限スクロール / 楽観的更新 / プリフェッチ / DevTools』。`データ取得まわりの定番ハマりどころ』 が一通り入っている。
- React 専用ではない。` Solid / Vue / Svelte / Angular』 でも同じ概念で使える。tRPC や Hono RPC の標準クライアントとしても採用されている。
React Query ってよく聞くけど、Redux とどう違うの? useEffect で fetch するのと何が違うの?』 今は TanStack Query って名前変わった?』 ── 2018年頃から急速に広まった React Query は、2022年に TanStack Query へ改名し、現在はマルチフレームワーク対応の `サーバ状態ライブラリ』 として定着しました。
ざっくり言うと、TanStack Query は サーバから取ったデータをキャッシュして、必要なときに同期する』</strong> 専門ライブラリです。サーバ状態(server state)』 を扱うことに特化していて、`クライアント側だけの状態(UI 状態など)』 とは別物として扱う、というのが設計思想の核心です。
この記事では、2026年5月時点の TanStack Query v5 系をベースに、仕組み・基本の書き方・Redux 等との違い・採用判断軸 を整理します。 仕様は活発に変化しているので、最終確認は 公式ドキュメント を見るのが安全です。
なぜ TanStack Query が必要になったか
なぜわざわざ専用ライブラリが必要なのか』 を useState + useEffect』 と比較すると分かりやすくなります。
// `素朴な』 書き方
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
return <p>{user.name}</p>;
}
これに対して、TanStack Query では:
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
return <p>{user.name}</p>;
}
行数の差以上の違い
素朴な書き方では ` 別画面に行って戻ると毎回再取得』 `タブを切り替えるたびに再フェッチ』 `エラーで自動再試行する』 などが手動 / 未対応』。TanStack Query はこれらをデフォルトで適切に処理する。
再取得のタイミング
` ウィンドウフォーカス時』 `ネットワーク再接続時』 `mount 時』 など、`いつ取り直すか』 を細かく設定できる。`画面を放置していたら古いデータが表示されたまま』 が起きづらい。
エラー / リトライ
` 失敗したら指数バックオフで自動リトライ』 が標準。`ネットワークの一瞬の不調』 で諦めない。
`非同期の細かい挙動を毎回手書きしなくていい』 のが、TanStack Query の最大の利点です。
キャッシュの考え方 — stale と fresh
TanStack Query のキャッシュは、` stale-while-revalidate』 という考え方に基づいています。
`fresh』 状態
取ったデータが `まだ新しい』 とみなされる期間。`staleTime』 で指定。デフォルトは 0 ms(取った瞬間に古くなる扱い)。
`cacheTime』 / `gcTime』
` どのコンポーネントからも使われなくなった後、何分間キャッシュを保持するか』。デフォルトは 5分。
refetch のトリガ
`stale なときに mount / window focus / network reconnect』 で自動再取得。`そんなに頻繁にいらない』 場合は `staleTime』 を伸ばす。
データを 絶対今の値』 か だいたい近い値』 のどちらで扱うか』 を staleTime』 でチームごとに調整するのが、TanStack Query の運用の中心です。
主要機能の一覧
`データ取得まわりの定番タスク』 が一通りそろっています。
| 機能 | API | 用途 |
|---|---|---|
| 取得 | `useQuery』 | GET 系。データを読む。 |
| 更新 | `useMutation』 | POST / PUT / DELETE 系。書き込み。 |
| 無効化 | `queryClient.invalidateQueries』 | ` この queryKey のキャッシュを古い扱いにする』。 |
| 楽観的更新 | `onMutate / setQueryData / onError』 | サーバ応答前に UI を仮更新。 |
| 無限スクロール | `useInfiniteQuery』 | ページネーション + `次へ』 を自動管理。 |
| プリフェッチ | `queryClient.prefetchQuery』 | ` 次の画面に行く前に先取り』。 |
| サスペンス連携 | `useSuspenseQuery』 | React Suspense と組み合わせる。 |
| DevTools | `@tanstack/react-query-devtools』 | キャッシュ状態を視覚化。 |
`データ取得で手で書きたくない処理は、まず TanStack Query にあるか確認する』 だけで、ほぼ何でも見つかる印象です。
Redux / Zustand / Context との違い
`なんで状態管理ライブラリの代わりに使うの?』 という疑問は、よく出ます。
| 軸 | Redux / Zustand / Context | TanStack Query |
|---|---|---|
| 得意領域 | クライアント状態(UI 状態、フォーム、モーダル) | サーバ状態(API から取ったデータ) |
| キャッシュ | 自前で実装 | 標準で提供 |
| 非同期 | middleware や thunk で手書き | 標準で提供 |
| 再取得 / 同期 | 手書き | 標準で提供 |
| 主な責務 | ` クライアントが持つべき状態』 | ` サーバが真実とする状態のキャッシュ』 |
| 組み合わせ | TanStack Query と併用がベスト | Redux / Zustand と併用がベスト |
要点は Redux 系と TanStack Query は競合ではなく、役割分担で併用する』</strong> ことです。サーバから取ったデータは TanStack Query、UI 状態(ダークモード / モーダル開閉)は Zustand / Context』 という棲み分けが2026 年現在の主流です。
楽観的更新の書き方
UX を高める典型機能 `楽観的更新』 の書き方を見ます。
const queryClient = useQueryClient();
const toggleLike = useMutation({
mutationFn: (postId) =>
fetch(`/api/posts/${postId}/like`, { method: 'POST' }),
onMutate: async (postId) => {
// 進行中の取得をキャンセル
await queryClient.cancelQueries({ queryKey: ['posts'] });
// 前の値を保持(ロールバック用)
const previous = queryClient.getQueryData(['posts']);
// 楽観的に更新
queryClient.setQueryData(['posts'], (old) =>
old.map(p => p.id === postId ? { ...p, liked: !p.liked } : p)
);
return { previous };
},
onError: (err, postId, context) => {
// 失敗したら前の値に戻す
queryClient.setQueryData(['posts'], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
},
});
楽観的更新とは
` サーバ応答を待たずに、UI を先に更新する』 こと。ボタンを押したら即色が変わる、いいねが即反映する、といった `早く感じる UX』 を実現する。
失敗時に `前の値に戻す』 ロジックが必須。TanStack Query の `onMutate → onError』 パターンで自然に書ける。
最終的な同期
`onSettled』 で ` 関連キャッシュを invalidate』 して、最終的にはサーバの値で上書きされるようにする。
React 19 との関係
React 19 の `useOptimistic』 は Server Actions と組み合わせて使う仕組み。`TanStack Query で全部完結する』 ケースと `React 標準の useOptimistic を使う』 ケースが棲み分かれつつある。
非同期処理の 応答までの空白』 をなくす』 のが楽観的更新の核心で、TanStack Query はこれを正攻法で書きやすくしてくれます。
いつ TanStack Query を使うか
採用判断の目安を整理します。
向いている
① React / Solid / Vue / Svelte の中〜大規模アプリ、② 複数画面で同じデータを表示する SPA、③ 非同期取得が多い管理画面 / SaaS、④ tRPC / GraphQL Code Generator の組み合わせ。
慎重に
① 純粋に App Router + RSC + Server Actions だけで完結する小規模アプリ(`useQuery が要らない』 ケースが増える)、② `1ページだけ』 のような極小プロジェクト、③ サーバ側で全部レンダリング前提のサイト。
RSC / Server Actions との関係
` データ取得の主役を RSC に寄せる』 場合、TanStack Query の必要性は下がる。ただし、`クライアント側のリアルタイム更新』 `フォーカス時の再取得』 が必要な場面では、依然として有力。
tRPC との相性
tRPC の React 統合は ` TanStack Query をベースに作られている』。`tRPC を使う = TanStack Query もセットで使う』 と言って差し支えない。
React 系のフルスタックアプリ』 を作るなら、いまも 必須スキル』 に近い位置にいます。
ハマりやすいポイント
便利な反面、現場で踏みやすい注意点も整理します。
①`queryKey』 設計
` queryKey が同じだとキャッシュを共有する』 ので、`配列の構造』 を最初に決めておく。` ['posts', { userId, page }]』 のような構造 がチーム標準的。
② staleTime デフォルト 0
` 取った瞬間 stale 扱い』 になり、毎回 refetch がトリガーされやすい。`staleTime: 1000 * 60』 などを `defaultOptions』 で設定すると、無駄な再取得が減る。
③ 無効化漏れ
` 更新したのにリストが古いまま』 が起きやすい。`useMutation の onSuccess で invalidateQueries』 を必ず入れる。
④ DevTools を入れていない
` どのキャッシュが今 stale で、どれが fresh か』 を視覚化できる DevTools が必須レベルに便利。開発時は必ず入れる。
`設定の妥当性を体感で詰める』 のが、TanStack Query の運用を綺麗にする最大のコツです。
AI 時代の TanStack Query
AI 連携の文脈でも TanStack Query は活躍します。
AI 応答のストリーミング
` useQuery で AI レスポンスを取りつつ、ストリーミングは別途処理』 のような構成が組める。`生成中の状態 + 過去履歴の表示』 を統一的に扱える。
プリフェッチで体感速度向上
` 次に必要そうな AI 応答』 をプリフェッチしておくことで、`クリック後の待ち時間』 を実質ゼロにできる。AI のレスポンス遅さを UX 上隠せる。
楽観的更新 × AI
` AI チャットで送信 → 即ユーザーメッセージを表示 → AI 応答が返ったら追加』 という典型的な UX を、`useMutation + 楽観的更新』 で素直に書ける。
tRPC + TanStack Query + AI
` AI 呼び出しを tRPC mutation として定義 → TanStack Query 統合で再取得 / 楽観的更新』 という構成は、AI 機能を持つアプリの定番パターン。
AI 応答の不確実な遅延』 を TanStack Query のキャッシュとプリフェッチ』 で吸収する、というのは AI 時代の UX 設計で頻出するパターンです。
TanStack Query に関するよくある質問
Q. React Query と TanStack Query は同じものですか?
A. 同じです』</strong>。2022 年にReact Query』 から TanStack Query』 に改名し、React 以外のフレームワーク(Solid / Vue / Svelte / Angular)もサポートする多目的ライブラリになりました。React Query』 は React 向けアダプタの呼称として残っています。
Q. SWR と TanStack Query、どちらを選ぶべきですか?
A. 用途と好みで分けます。 SWR は Vercel 製の軽量・シンプル路線』</strong>、<strong> TanStack Query は機能が豊富で 何でもできる』 路線』</strong>。小さなアプリで軽さ重視』 なら SWR、`本格的な機能と DevTools が欲しい』 なら TanStack Query、というのがざっくりの選び方です。
Q. RSC と Server Actions だけで完結する場合、TanStack Query は不要ですか?
A. ケースバイケース』</strong>です。完全に読みは RSC、書きは Server Actions、再取得は revalidatePath』 で済む場合は不要です。ただし、Realtime な再取得』 フォーカス再取得』 `楽観的更新』 が必要な場面では、TanStack Query を併用する設計が依然として有効です。
Q. tRPC と TanStack Query は必ずセットですか?
A. React で tRPC を使う場合は事実上セット』</strong>です。tRPC の React Hooks』 は内部的に TanStack Query を使っており、useQuery / useMutation』 と同じ感覚で trpc.x.useQuery()』 が使えます。
Q. キャッシュ無効化を全部書くのが面倒です。
A. queryKey の階層を意識して、まとめて invalidate する』</strong>のが定石です。queryClient.invalidateQueries({ queryKey: ['posts'] })』 だけで `['posts', ...]』 系すべてのクエリを無効化できます。
Q. SSR ではどう使いますか?
A. サーバ側でデータをフェッチ → dehydrate してクライアントに渡す → hydrate』</strong>のパターンが基本です。Next.js / Remix での専用ヘルパー(HydrationBoundary』 等)があります。
Q. TanStack Query を学ぶ最短ルートは?
A. ① useQuery』 で1つ取得、② useMutation』 で1つ更新、③ queryClient.invalidateQueries』 で再取得、④ DevTools をインストールして挙動を観察、の4段階が王道です。動かしながらキャッシュ状態を見る』 のが、TanStack Query の理解を加速する最大のコツです。