状態管理(応用編:Global State & Server State)
前章では、useState や Props による基本的な状態管理を学びました。
ここでは、アプリが大規模になったり、Next.js 特有の機能を活かしたりするための「応用的な状態管理」について解説します。
1. Global State (Context API)
Props Drilling(バケツリレー)を解決するための、React 標準の機能です。
「離れたコンポーネント同士でデータを共有したい」時に使います(例:テーマ設定、ログインユーザー情報)。
使い方の流れ
- Context を作成する
- Provider で囲む
- useContext で値を取り出す
src/contexts/ThemeContext.tsx"use client"; import { createContext, useContext, useState } from "react"; // 1. Contextの作成 const ThemeContext = createContext("light"); // 2. Providerコンポーネント(親) export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={theme}> <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> Toggle Theme </button> {children} </ThemeContext.Provider> ); } // 3. 値を使うためのカスタムフック(子) export function useTheme() { return useContext(ThemeContext); }
注意点:Context の値が変わると、それを使っているコンポーネントは 全て再レンダリング されます。頻繁に更新される巨大なデータには向きません。
2. URL as State (URL状態管理)
Next.js (App Router) で最も重要な概念の一つです。 検索フォームの入力内容や、ページネーションのページ数などは、useState ではなく URL(クエリパラメータ) で管理すべきです。
なぜ URL で管理するのか?
- シェアできる : URL をコピーして友人に送れば、同じ検索結果を見てもらえる。
- ブラウザバックできる : 「戻る」ボタンで前の検索結果に戻れる。
- リロードしても消えない :
useStateはリロードすると消えるが、URL は残る。
実装例 (useSearchParams)
src/app/search/page.tsx"use client"; import { useSearchParams, useRouter, usePathname } from "next/navigation"; export default function SearchPage() { const searchParams = useSearchParams(); const { replace } = useRouter(); const pathname = usePathname(); // URLから ?q=... を取得 const query = searchParams.get("query")?.toString() || ""; const handleSearch = (term: string) => { const params = new URLSearchParams(searchParams); if (term) { params.set("query", term); } else { params.delete("query"); } // URLを書き換える(ページ遷移せずに) replace(`${pathname}?${params.toString()}`); }; return ( <div> <input defaultValue={query} onChange={(e) => handleSearch(e.target.value)} className="border p-2" /> <p>検索キーワード: {query}</p> </div> ); }
3. Server State vs Client State
状態は「どこにあるデータか」で2種類に分けられます。
- Client State : UI の状態。モーダルの開閉、入力中の文字など。(これまで解説したもの)
- Server State : データベースにあるデータ。記事一覧、ユーザー情報など。
Server State は、API から取得してキャッシュしたり、更新したりする必要があります。
Next.js App Router では、 Server Components で直接 await fetch() するのが基本 ですが、Client Component で扱う場合はライブラリを使うのが一般的です。
代表的なライブラリ
- SWR (Vercel製): シンプルで軽量。
- TanStack Query (React Query) : 高機能。
これらを使うと、「データの取得中」「エラー」「キャッシュの更新」などを簡単に管理できます。
4. 外部ライブラリ (Zustand, Recoil, Jotai)
Context API よりも高機能な Global State 管理が必要な場合に使います。
- Zustand (ズスタンド) : 今もっとも人気がある。シンプルで使いやすい。
- Jotai (ジョータイ) : Atom(原子)という単位で状態を管理する。
- Recoil (リコイル) : Facebook製だが、最近は更新が停滞気味。
いつ使うべき?
「アプリ全体で複雑なデータフローがある」「Context だと再レンダリングのパフォーマンス問題が起きる」といった場合に導入を検討してください。小規模なアプリでは不要なことが多いです。
まとめ
Next.js アプリの状態管理は、以下の優先順位で考えると綺麗に設計できます。
- URL : 検索条件やフィルタなど、シェアすべき情報は URL に入れる。
- Server State : サーバーのデータは Server Components か SWR/React Query で扱う。
- Local State (useState/useReducer) : 特定のコンポーネントだけで完結するUIの状態。
- Global State (Context/Zustand) : 上記に当てはまらず、複数の場所で共有したいデータ。
適材適所でツールを使い分けましょう!
更新履歴
- 2025-01-04 : ページのレイアウト崩れ・誤字を修正しました。