状態管理ライブラリ概説(Redux Toolkit / Zustand など)
なぜ専用ライブラリが必要になるのか?
useState / useReducer / Context だけでも、ある程度までは頑張れますが、規模が大きくなると次のような問題が出てきます:
- どのコンポーネントが「どの Context」を使っているか分かりにくい
- 「この画面の state が他のどこから更新されているのか?」の追跡が難しい
- コンポーネントをまたいだロジック(例:認証情報、カート情報、テーマ設定など)が増えすぎて理解が難しい
そこで登場するのが 状態管理ライブラリ です。
代表的なもの:
- Redux Toolkit
- Zustand
- (他に Recoil, Jotai, MobX など)
ここでは「まずこの2つの性格を押さえる」だけで十分です。
Redux Toolkit(RTK)
位置づけ
- 「Redux」という古参ライブラリを、現代風に使いやすくラップしたもの
- いわゆる「グローバルストア型」の代表
- 「action のログ」「状態の履歴」「開発ツール」の強さが売り
メリット
- 状態を1か所(store)に集約し、更新の流れが明確
- Redux DevTools で
- どの action が dispatch されたか
- その結果 state がどう変わったか
を時系列で追える(タイムトラベルも可能)
- 大規模・チーム開発で「パターンを統一」しやすい
コード例
// counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
type CounterState = { value: number }
const initialState: CounterState = { value: 0 }
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value += 1
},
add(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
})
export const { increment, add } = counterSlice.actions
export default counterSlice.reducer// store.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})// Counter.tsx
import { useSelector, useDispatch } from 'react-redux'
import { increment } from './counterSlice'
import type { RootState, AppDispatch } from './store'
export function Counter() {
const value = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch<AppDispatch>()
return (
<div>
<p>{value}</p>
<button onClick={() => dispatch(increment())}>+1</button>
</div>
)
}「action を投げる ⇒ reducer が受けて state を変える」という明確な流れがある代わりに、少し儀式的なコードがあります。
Zustand
位置づけ
- 「必要最低限でいい感じにグローバル state を持ちたい」派
- Context の拡張版のような軽さ
- 個人開発・中規模プロジェクトとの相性が良い
メリット
- APIがシンプルで、カスタムフックっぽく書ける
- Reduxよりボイラープレートが少ない
- TypeScriptとの相性も良い
コード例
// useCounterStore.ts
import { create } from 'zustand'
type CounterState = {
value: number
increment: () => void
add: (n: number) => void
}
export const useCounterStore = create<CounterState>((set) => ({
value: 0,
increment: () => set((s) => ({ value: s.value + 1 })),
add: (n) => set((s) => ({ value: s.value + n })),
}))// Counter.tsx
import { useCounterStore } from './useCounterStore'
export function Counter() {
const { value, increment } = useCounterStore()
return (
<div>
<p>{value}</p>
<button onClick={increment}>+1</button>
</div>
)
}見た目はほぼ「useState を外だししただけ」なので、学習コストが低いのが強みです。
どっちを選べばいい?
ざっくりした指針として:
- まずは Context + useReducer を十分に使いこなす
- それでもしんどくなってきたら…
- 状態が極端に多くて、ログをがっつり追いたい
⇒ Redux Toolkit - そこまで巨大じゃないが、「Contextだとキツい」
⇒ Zustand
- 状態が極端に多くて、ログをがっつり追いたい
個人開発なら「Context ⇒ Zustand ⇒ 必要ならRTK」の順が扱いやすいです。
データ取得ライブラリ(React Query / SWR など)
なぜ必要になるのか?
素のReactでAPIを使うと、毎回このパターンを書きがちです:
useEffectでfetchloading/error/dataの3状態を state 管理- 再取得(再フェッチ)をしたいときにロジックが複雑になる
- キャッシュも自前で書く必要あり
同じようなコードを画面ごとにコピペすると、バグも増えるし、挙動の一貫性も失われます。
そこで 「サーバーデータ専用の状態管理ライブラリ」 という考え方が出てきます。
React Query(TanStack Query)
コンセプト
useQuery/useMutationというフックを使って、データ取得・更新・キャッシュ・再取得・ローディング・エラー管理を全部任せる ライブラリ
コード例
import { useQuery } from '@tanstack/react-query'
import { fetchTodos } from './api'
export function TodoList() {
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
})
if (isLoading) return <p>読み込み中...</p>
if (error) return <p>エラーが発生しました</p>
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}特徴的なポイント:
queryKeyで「このデータは何か」を識別 ⇒ キャッシュキーになるqueryFnが一度成功すると、その結果がキャッシュされる- 別コンポーネントで同じ
queryKeyを使えば、キャッシュを共有できる - 「バックグラウンドで新鮮なデータに更新する」など、再取得の戦略も柔軟
「サーバー状態はReact Queryに任せて、ローカル状態だけReactで持つ」という設計にすると、かなり整理されます。
SWR
概要
- Vercel(Next.jsを作ってる会社)が作ったデータ取得ライブラリ
- 名前は
Stale-While-Revalidate戦略が由来- まずはキャッシュ(stale)を即返す
- 裏で最新データを取りに行き、終わったら差し替える
コード例
import useSWR from 'swr'
import { fetcher } from './api'
export function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher)
if (isLoading) return <p>読み込み中...</p>
if (error) return <p>エラーが発生しました</p>
return (
<ul>
{data.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
)
}useSWR(key, fetcher)の形で使う- React Queryと思想はかなり近い
React Query vs SWR
両方とも優秀なので、ぶっちゃけ どちらでも良い です。
強いて言えば:
- 強力な機能を網羅的に使いたい ⇒ React Query
- Next.js + Vercel 文化圏に寄せたい ⇒ SWR
くらいの感覚で覚えておけばOKです。
UIコンポーネントライブラリ(MUI / Chakra UI / Ant Design など)
どんなものか?
「ボタン・フォーム・ダイアログ・テーブル・レイアウトなどを、そこそこ綺麗なデザインで一通り揃えてくれる」 ライブラリです。
メリット:
- 自前でCSSを書く量を大幅に減らせる
- コンポーネント単位で一貫したデザインが保てる
- Formやダイアログ、モーダルなど「めんどいUI」が最初から用意されている
デメリット:
- デザインがライブラリ色になる(独自ブランドを出しづらい)
- カスタマイズがやや複雑なこともある
- バンドルサイズ増加の要因になりうる
MUI(Material UI)
- Googleの Material Design に準拠
- コンポーネント数が多く、細かい制御が効く
- 日本語の情報も多め
コード例:
import Button from '@mui/material/Button'
function App() {
return <Button variant="contained">送信</Button>
}「企業っぽい見た目」「管理画面」「ダッシュボード」 にはかなり強いです。
Chakra UI
- 柔らかいデザインと分かりやすいAPIが特徴
Box,Stackなどのレイアウトコンポーネントが使いやすい
import { Button, Stack } from '@chakra-ui/react'
function App() {
return (
<Stack direction="row" spacing={4}>
<Button colorScheme="teal">OK</Button>
<Button variant="outline">キャンセル</Button>
</Stack>
)
}「個人サービスっぽい柔らかいUI」「プロトタイプ」に向いています。
Ant Design
- 中国発。BtoB向けの業務システム感の強いデザイン
- テーブルやフォームなど、業務UIに強い印象
import { Button } from 'antd'
function App() {
return <Button type="primary">送信</Button>
}「管理画面・ダッシュボード・業務系ツール」をサクッと作るにはかなり強力です。
どう選ぶか?
- 「自分でデザインしたい・Tailwindなどを使いたい」
⇒ UIライブラリは入れず、ヘッドレスUI(Headless UI, Radix UI)+CSSで組む - 「とりあえずそれっぽく見せたい」
⇒ MUI か Chakra UI のどちらかに乗る - 「エンタープライズ系の業務UIを作る」
⇒ Ant Design も選択肢
最初の1個としては、MUIかChakraのどちらかを触ってみて「合うほう」を選べばOKです。
Next.jsなどのメタフレームワーク(SSR/SSGの世界)
React単体とNext.jsの違い
React自体は、「コンポーネントを使ってUIを組み立てるためのライブラリ」であって、
- ルーティング
- サーバーサイドレンダリング(SSR)
- 静的サイト生成(SSG)
- APIサーバー
などは担当外です。
そこをまとめて面倒見てくれるのが Next.js のような「メタフレームワーク」です。
SSR / SSG の概念
- CSR(Client-Side Rendering)
- 素のReactでよくあるスタイル
- ブラウザがJSを読み込んでから画面を描画
- SSR(Server-Side Rendering)
- サーバーでHTMLをレンダリングしてからクライアントに返す
- 初回表示が速くてSEOに強い
- SSG(Static Site Generation)
- ビルド時にHTMLを作って、CDNから配信
- ブログやドキュメントサイトに向く
Next.jsはこれらをページごとに選べるのが強力です。
Next.js のざっくり機能
- ファイルベースルーティング
app/blog/page.tsxみたいなファイルで自動的にルートが決まる
- サーバーコンポーネント / クライアントコンポーネント
- 「このコンポーネントはサーバーでレンダする」「これはブラウザ側」などを分けられる
- API Routes
app/api/xxx/route.tsみたいに書いて、簡単なバックエンドを同じプロジェクト内に作れる
- 画像最適化、Headの管理、レイアウト管理など、Webアプリに必要な周辺を面倒見てくれる
イメージとしては、
React が「エンジン + 車体の一部」
Next.js が「車として走れる状態まで組み上げたフレーム」
という感じです。
おすすめの学習順
- まずは 素のReactでSPAを1つ作る
- state, props, hooks, routing の基礎を固める
- その後、Next.jsに移って
- SSR / SSG やフルスタック的な構成(簡易API)を学ぶ
いきなりNext.jsから入ることも可能ですが、「React本体の理解」と「Next.jsの仕組み」が頭の中でごちゃっとしやすいので、「React単体で最低1つアプリを作る ⇒ Next.jsへ」という二段構えのほうが、理解は早いです。
まとめ
- 状態管理ライブラリ
- Redux Toolkit:大規模向き、履歴・DevToolsが強い
- Zustand:軽量・シンプル、個人開発や中規模に向く
- データ取得ライブラリ
- React Query / SWR:
useEffect + fetchの面倒を丸ごと引き受けてくれるサーバー状態管理 - キャッシュ・再取得・ローディング・エラーを統一的に扱える
- React Query / SWR:
- UIコンポーネントライブラリ
- MUI / Chakra UI / Ant Design などでボタン・フォーム・ダイアログなどを一気に揃えられる
- 「自分でCSS書き込むか」「ライブラリに任せるか」の選択
- メタフレームワーク(Next.js)
- ルーティング・SSR・SSG・APIなど、Webアプリ全体の構成を引き受けてくれる
- React単体の次のステップとして「フルスタック寄りの開発」に踏み込める
コメント