Zustand vs Jotai — どちらを選ぶべきか?
Zustand vs Jotai — どちらを選ぶべきか?
どちらも軽量でシンプルなReact状態管理ライブラリとして人気を集めているZustandとJotai。設計思想の違いを理解して、プロジェクトに合った選択をしよう。
概要
Zustand
作者: pmndrs(Poimandres)
バンドルサイズ: 約1KB
設計思想: 集約型(Store型)
単一のストアにstateとactionをまとめる設計。Redux的な発想をシンプルに再解釈したライブラリ。ボイラープレートが少なく、学習コストが低いのが特徴。
Jotai
作者: pmndrs(Poimandres)
バンドルサイズ: 約3KB
設計思想: 分散型(Atom型)
状態をatomという単位で細かく管理するボトムアップ設計。RecoilにインスパイアされつつAPIをよりシンプルに実現している。
コード比較
カウンターの例
Zustand — store定義 + 使用
import { create } from 'zustand'
const useStore = create(set => ({
count: 0,
inc: () => set(s => ({ count: s.count + 1 })),
}))
// コンポーネント内
const { count, inc } = useStore()
Jotai — atom定義 + 使用
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
// コンポーネント内
const [count, setCount] = useAtom(countAtom)
const inc = () => setCount(c => c + 1)
非同期fetchの例
Zustand — 非同期アクション
const useStore = create(set => ({
user: null,
loading: false,
fetchUser: async (id) => {
set({ loading: true })
const res = await fetch(`/api/users/${id}`)
const user = await res.json()
set({ user, loading: false })
},
}))
Jotai — 非同期atom(Suspense連携)
const userIdAtom = atom(1)
const userAtom = atom(async (get) => {
const id = get(userIdAtom)
const res = await fetch(`/api/users/${id}`)
return res.json()
})
// Suspenseで自動ローディング管理
派生stateの例
Zustand — selectorで派生値を計算
const useStore = create(set => ({
items: [],
// ...actions
}))
const total = useStore(
s => s.items.reduce((acc, i) => acc + i.price, 0)
)
Jotai — 派生atomを宣言的に定義
const itemsAtom = atom([])
const totalAtom = atom(
(get) => get(itemsAtom).reduce((acc, i) => acc + i.price, 0)
)
const total = useAtomValue(totalAtom)
詳細比較
観点 | Zustand | Jotai |
|---|---|---|
設計思想 | 単一ストアに集約。Fluxアーキテクチャに近い | atomを分散配置。ボトムアップ設計 |
学習コスト | 非常に低い。 | 低いが、派生・非同期atomに慣れるまで少し時間がかかる |
再レンダリング | selectorで最適化可能(手動指定が必要) | atomが変化したコンポーネントのみ自動で最適化 |
TypeScript | 型推論が効きやすい。 |
|
Suspense対応 | 公式サポートなし(ミドルウェアで対応可) | 非同期atomがそのままSuspenseと統合できる |
DevTools | Redux DevTools対応。デバッグしやすい | Jotai DevToolsが提供されているが発展途上 |
ミドルウェア | persist・immer・subscribeWithSelectorなど豊富 |
|
React外での使用 |
| 基本的にReact(またはProvider)が必要 |
こんな場合に使い分けよう
Zustand が向いているケース
- グローバルなUIステート(モーダル、トースト、サイドバー)の管理
- 認証情報やユーザー設定の一元管理
- ReduxやVuexから移行したい
- サーバーサイドや非ReactコードからもStateを操作したい
- チームが大きく、ストアを一元管理したい
Jotai が向いているケース
- 細かい粒度の状態が多く、分散して管理したい
- 非同期データフェッチをSuspenseで宣言的に扱いたい
- 状態間の依存関係が複雑(派生atomが便利)
- コンポーネントごとに独立したStateを持ちたい
- テスト時にatomを差し替えてモックしたい
まとめ
Zustand を選ぶ場合
ストア設計の経験がある、グローバルStateをシンプルに管理したい、React外との連携がある、チーム開発でデバッグ体験を重視したい場合に向いている。
Jotai を選ぶ場合
Reactらしいボトムアップ思考が好き、Suspenseと組み合わせて非同期処理を宣言的に書きたい、細かい状態の再レンダリング最適化を自動化したい場合に向いている。
どちらも同じpmndrsチームが開発しており、品質・メンテナンスともに信頼できる選択肢だ。設計思想の違いを理解した上で、プロジェクトの規模や要件に合わせて選ぼう。