この回では、React 19で登場した新しいフックや、現代のReact開発におけるサーバー状態管理のベストプラクティスである TanStack Query について解説します。
💡 React 19 / Server Components時代の最新Hooks
Server Componentsや非同期処理の管理を容易にするために、新しいHooksとコンポーネントが導入されました。
1. use — 非同期データの簡易取得
useフックは、コンポーネント内で Promise や Thenable を直接受け取り、非同期処理を簡単に扱えるようにする機能です。
Server Componentsと組み合わせることで、データをprops経由で渡す必要が減り、コードが簡潔になります。
例:
import { use } from "react";
async function fetchUser() {
const res = await fetch("/api/user");
return res.json();
}
function UserProfile() {
const user = use(fetchUser()); // Promise を直接受け取る
return <p>{user.name}</p>;
}
ポイント
- Promise が解決されるまで 自動で Suspense の fallback にフォールバックします。
- Client Component 内でも使用可能ですが、必ず Suspense でラップする必要があります。
2. Suspense — 非同期読み込み UI
非同期データやコンポーネントの読み込み時に、ローディング表示を簡単に実装できるコンポーネントです。use フックと組み合わせることで、データ取得完了まで UI を待機させることができます。
例:
import { Suspense } from "react";
// UserProfileコンポーネントが内部で use を使ってデータを取得していると仮定
<Suspense fallback={<p>Loading user...</p>}>
<UserProfile />
</Suspense>
3. フォーム・アクション管理のためのHooks(React 19)
Server Actionsやフォーム送信時のユーザー体験を向上させるために、以下のHooksが導入されました。
useFormStatus — フォーム送信状態の取得
フォーム送信中かどうかを取得し、UI に反映させるためのフックです。
例:
const { pending } = useFormStatus();
<button type="submit" disabled={pending}>
{pending ? "送信中..." : "送信"}
</button>
useActionState — アクション実行と state 管理を同時に行う
旧 useFormState が改名されたもので、サーバーアクション実行時の state 変化を管理できます。アクション実行時の状態や結果メッセージをクライアント側で受け取って管理するために使います。
例:
const [state, formAction, pending] = useActionState(action, 0);
<form action={formAction}>
<button disabled={pending}>+1</button>
</form>
<p>現在の値: {state}</p>
useOptimistic — 楽観的UI(サーバー待たず先にUI更新)
サーバー処理の完了を待たずに 一旦 UI を先に更新する(楽観的更新) ためのフックです。チャット投稿や「いいね」のように、すぐにUIを更新することでレスポンス性を高めます。
例:
const [messages, addOptimisticMessage] = useOptimistic(
initialMessages,
(state, newMessage) => [...state, newMessage]
);
function handleSend(message) {
addOptimisticMessage(message); // UI 先行更新
submitToServer(message); // サーバー送信
}
💻 useEffect から卒業 — TanStack Queryでサーバー状態管理の正解を学ぶ
React で API 通信をするとき、昔は useEffect + fetch が定番でしたが、現在は TanStack Query(旧 React Query) がサーバー状態管理のデファクトスタンダードとなっています。
useEffect + fetch の根本的な課題
useEffect は、本来「UIの副作用」を扱うためのHooksであり、以下のようなサーバー状態管理の煩雑なタスクには向いていません。
-
状態の肥大化:
loading / error / dataの 3つのState を必ず手動で管理する必要がある。 - キャッシュの欠如: 毎回再フェッチが実行され、無駄な通信が増える。
- 機能の不足: 自動リフェッチ、リトライ、ウィンドウ復帰時の最新化など、モダンな機能はすべて自作が必要。
TanStack Query(useQuery)が解決すること
TanStack Queryは、API通信に関わる面倒な処理のほぼすべてを自動化する サーバー状態の完全管理ツール です。
| 機能 | useEffect | TanStack Query |
|---|---|---|
| ローディング/エラー管理 | 手実装 | 自動 |
| キャッシュ管理 | × | 自動 |
| 複数コンポーネントでデータ共有 | × | ○ |
| ウィンドウ復帰で最新化 | × | ○ |
| リトライ・バックグラウンド更新 | × | ○ |
実装例の比較(TanStack Query)
useEffectで数十行必要だった処理が、TanStack Queryでは非常にシンプルになります。
import { useQuery } from "@tanstack/react-query";
export default function Users() {
// data, isLoading, error の状態を自動で受け取る
const { data, isLoading, error } = useQuery({
queryKey: ["users"], // データの一意なキー
queryFn: () => fetch("/api/users").then((res) => res.json())
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
まとめ:状態管理ツールの使い分け
| シナリオ | 推奨ツール | 理由 |
|---|---|---|
| APIなどサーバーからデータを取得する | TanStack Query | サーバー状態管理に必要な機能をすべて備えているため。 |
| UI/設定などのグローバル状態 | Zustand / Context | 非同期に依存しないローカル状態の共有に適しているため。 |
| ローカル状態の管理だけ | useState / useReducer / useEffect | コンポーネント固有の簡易な状態管理のため。 |
現代のベストプラクティスは 「Zustand × TanStack Query × Next.js」 の組み合わせが非常に強力です。