Skip to content

状態管理ライブラリ完全比較

状態管理ライブラリの詳細な比較と、実務で使える選定基準を詳しく解説します。

1. グローバル状態管理ライブラリの比較

Section titled “1. グローバル状態管理ライブラリの比較”
ライブラリ学習コストボイラープレート規模デバッグパフォーマンスバンドルサイズTypeScript
Context API低い少ない小規模容易中程度0KB(標準)良好
Redux高い多い大規模優秀高い約12KB優秀
Zustand非常に低い非常に少ない小〜中規模容易高い約1KB優秀
Recoil低い少ない小〜大規模容易高い約14KB優秀
Jotai非常に低い少ない小〜大規模容易高い約3KB優秀

Context API:

// セットアップが簡単
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

Redux:

// セットアップが複雑
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
const store = configureStore({
reducer: {
// reducers
},
});
function App() {
return (
<Provider store={store}>
<YourApp />
</Provider>
);
}

Zustand:

// セットアップが非常に簡単
import { create } from 'zustand';
const useStore = create((set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
}));

Context API:

// 問題: すべての子コンポーネントが再レンダリングされる
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
// themeが変更されると、userを使用しているコンポーネントも再レンダリングされる
return (
<ThemeContext.Provider value={{ theme, setTheme, user, setUser }}>
{children}
</ThemeContext.Provider>
);
}

Zustand:

// 解決: 細かい粒度での選択が可能
const useStore = create((set) => ({
theme: 'light',
user: null,
setTheme: (theme) => set({ theme }),
setUser: (user) => set({ user }),
}));
// themeが変更されても、userを使用しているコンポーネントは再レンダリングされない
function UserProfile() {
const user = useStore((state) => state.user);
return <div>{user?.name}</div>;
}

Redux:

// Redux DevToolsが優秀
import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: {
// reducers
},
devTools: process.env.NODE_ENV !== 'production',
});
// タイムトラベルデバッグが可能

Zustand:

// Zustand DevToolsも利用可能
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{ name: 'CounterStore' }
)
);

2. サーバー状態管理ライブラリの比較

Section titled “2. サーバー状態管理ライブラリの比較”
ライブラリ学習コスト機能デバッグバンドルサイズコミュニティTypeScript
SWR低い基本的容易約4KBVercel中心良好
TanStack Query中程度豊富優秀約13KB大規模優秀

SWR:

// URL文字列をキーとして使用
import useSWR from 'swr';
const { data } = useSWR('/api/users/1', fetcher);

TanStack Query:

// 配列をキーとして使用(柔軟)
import { useQuery } from '@tanstack/react-query';
const { data } = useQuery({
queryKey: ['user', userId, { status: 'active' }],
queryFn: () => fetchUser(userId, { status: 'active' }),
});

SWR:

// 条件付きフェッチング
const { data: user } = useSWR(
userId ? `/api/users/${userId}` : null,
fetcher
);
const { data: posts } = useSWR(
user ? `/api/users/${userId}/posts` : null,
fetcher
);

TanStack Query:

// enabledオプションで条件付き実行
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
enabled: !!userId,
});
const { data: posts } = useQuery({
queryKey: ['posts', userId],
queryFn: () => fetchUserPosts(userId),
enabled: !!user, // より明確
});

適しているプロジェクト:

  • 大規模なエンタープライズアプリケーション
  • タイムトラベルデバッグが必要
  • 厳格な状態管理が必要
  • 複雑な状態遷移がある
  • チーム間の一貫性が必要

実装例:

// Redux Toolkitを使用
import { configureStore, createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: { user: null },
reducers: {
setUser: (state, action) => {
state.user = action.payload;
},
},
});
export const store = configureStore({
reducer: {
user: userSlice.reducer,
},
});

適しているプロジェクト:

  • シンプルなグローバル状態
  • 学習コストを抑えたい
  • ボイラープレートを最小化したい
  • 小〜中規模のプロジェクト
  • 迅速な開発が必要

実装例:

// Zustandを使用
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
user: null,
increment: () => set((state) => ({ count: state.count + 1 })),
setUser: (user) => set({ user }),
}));

適しているプロジェクト:

  • アトミックな状態管理が必要
  • Reactの思想に近いアプローチ
  • コンポーネントベースの開発
  • 細かい粒度での状態管理
  • パフォーマンスが重要

実装例:

// Jotaiを使用
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
const nameAtom = atom('John');
function Counter() {
const [count] = useAtom(countAtom);
return <div>{count}</div>;
}

適しているプロジェクト:

  • サーバー状態の管理
  • キャッシングと再取得が必要
  • APIとの連携が多い
  • データの同期が必要
  • 複雑なデータフェッチング

実装例:

// TanStack Queryを使用
import { useQuery } from '@tanstack/react-query';
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data?.map(user => <div key={user.id}>{user.name}</div>)}</div>;
}

要件:

  • 小規模チーム(2-3人)
  • 迅速な開発が必要
  • 学習コストを抑えたい

選定:

  • グローバル状態: Zustand
  • サーバー状態: SWR

理由:

  • 学習コストが低い
  • セットアップが簡単
  • 開発速度が速い

ケース2: エンタープライズアプリケーション

Section titled “ケース2: エンタープライズアプリケーション”

要件:

  • 大規模チーム(10人以上)
  • 厳格な状態管理が必要
  • タイムトラベルデバッグが必要

選定:

  • グローバル状態: Redux Toolkit
  • サーバー状態: TanStack Query

理由:

  • 厳格な状態管理
  • 優秀なデバッグツール
  • チーム間の一貫性

要件:

  • 高いパフォーマンス要件
  • 細かい粒度での再レンダリング制御

選定:

  • グローバル状態: Jotai、Recoil
  • サーバー状態: TanStack Query

理由:

  • アトミックな状態管理
  • 細かい粒度での最適化
  • パフォーマンスが高い

これで、状態管理ライブラリの詳細な比較と選定基準を理解できるようになりました。