はじめに
Reactアプリを作っていると、コンポーネントをまたいで「状態(state)」を共有したい場面が増えてきます。
そのとき、よく比較対象として出てくるのが、
- Redux
- Zustand
です。
この記事では、
- Reduxの発想
- Zustandの発想
- 両者の思想的な違い
- 「なぜZustandを選ぶのか」という判断軸
を、公式ドキュメント寄りの情報をベースに整理していきます。
個人的な感想はできるだけ減らし、「事実」と「設計思想」を軸にまとめています。
1 Reduxの思想を整理
1-1. Reduxの「3つの原則」
Reduxには、公式ドキュメントで紹介されている
「Three Principles(3つの原則)」があります。
1 状態は1つのストアにまとめる
Reduxでは、アプリ全体の状態を「1つのストア(store)」で管理します。
つまり、
- ユーザー情報
- ログイン状態
- 通知
- カート情報
などを、1つの大きな状態ツリーとして持ちます。
この考え方によって、
- 状態がどこにあるか分かりやすい
- 状態の流れを追跡しやすい
というメリットがあります。
2 状態変更は「アクション」で表現する
Reduxでは、状態を直接変更しません。
代わりに、
「何が起きたか」
を、アクション(action)として表現します。
たとえば:
{ type: "INCREMENT" }
{ type: "LOGIN", payload: "user123" }
のような形です。
ここで重要なのは、
- 「どう変更するか」ではなく
- 「何が起きたか」を表現する
という点です。
3 状態変更はリデューサーで行う
Reduxでは、アクションを受け取ったリデューサー(reducer)が、
- 現在の状態
- 渡されたアクション
をもとに、新しい状態を返します。
function counterReducer(state, action) {
switch (action.type) {
case "INCREMENT":
return {
count: state.count + 1
}
default:
return state
}
}
この仕組みによって、
- どこで状態が変わったか
- どんな順番で変更されたか
を追いやすくなります。
つまりReduxは、
「状態変更の交通整理を厳密に行う設計」
と言えます。
1-2. Reduxと非同期処理
Reduxでは、API通信などの非同期処理をそのまま reducer に書きません。
代わりに、
- redux-thunk
- redux-saga
などのミドルウェアを使います。
代表的な redux-thunk では、
const fetchUser = () => async (dispatch) => {
dispatch({ type: "FETCH_START" })
const user = await api.getUser()
dispatch({
type: "FETCH_SUCCESS",
payload: user
})
}
のように、
- 非同期処理を実行
- 途中で dispatch
- 状態変更を段階的に管理
します。
この設計によって、
- 非同期処理の流れを統一しやすい
- チームで書き方を合わせやすい
という特徴があります。
2 Zustandの思想を整理
2-1. Zustandの中心となる考え方
Zustandでは、主に次のAPIが中心になります。
createsetgetuseStore
かなりシンプルです。
イメージとしては:
| API | 役割 |
|---|---|
| create | 状態と操作を作る |
| set | 状態を更新する |
| get | 現在の状態を読む |
| useStore | コンポーネントで状態を使う |
という構造です。
2-2. Zustandの設計思想
Zustandの公式では、
- simple
- fast
- scalable
という特徴が強調されています。
実際のコードはかなり短く書けます。
import { create } from "zustand"
const useCounterStore = create((set) => ({
count: 0,
increment: () =>
set((state) => ({
count: state.count + 1
}))
}))
コンポーネント側では:
const count = useCounterStore((state) => state.count)
のように必要な値だけ取得します。
2-3. Zustandの特徴
Zustandの特徴として大きいのが、
- Provider が不要
- コンポーネント外からも状態に触れる
という点です。
たとえば:
useCounterStore.getState()
で現在状態を取得できます。
また:
useCounterStore.subscribe()
で状態変更を監視することも可能です。
つまり、
「Reactの外側からも扱いやすい状態管理」
という特徴があります。
3 ReduxとZustandの思想的な違い
ここからは、両者の違いを項目ごとに整理します。
3-1. 状態をどこに置くか
| 項目 | Redux | Zustand |
|---|---|---|
| 状態の管理 | 1つの巨大ストア | 複数ストアも可能 |
| 発想 | 全体を一元管理 | 必要な単位で分割 |
| スケール感 | 中央集権型 | 分散型 |
Reduxは、
「状態は中央に集約する」
という発想が強いです。
一方Zustandは、
「必要な場所に必要な状態を置く」
という考え方に近いです。
3-2. 状態をどう変更するか
| 項目 | Redux | Zustand |
|---|---|---|
| 更新方法 | action → reducer | set |
| ルール | 厳密 | 柔軟 |
| 書き方 | 定型化されやすい | 自由度が高い |
Reduxでは、
- action
- reducer
- immutable な更新
などのルールが明確です。
そのため、
- 履歴追跡
- デバッグ
- チーム開発
に強い設計になります。
一方Zustandは、
set({ count: 10 })
のように直接更新できます。
書く量は減りますが、
- チームでルールを決める
- 設計を揃える
必要があります。
3-3. Reactとの距離感
| 項目 | Redux | Zustand |
|---|---|---|
| React依存 | 比較的薄い | フック中心 |
| 主な利用方法 | Provider + hooks | useStore |
| 外部利用 | 可能 | より直接的 |
Reduxは、
React以外でも使える汎用状態管理
という側面があります。
一方Zustandは、
Reactのフック文化に強く寄った設計
です。
そのため、
- Reactらしく書きやすい
- Providerのノイズが少ない
というメリットがあります。
3-4. 学習コストとチーム開発
| 項目 | Redux | Zustand |
|---|---|---|
| 学習コスト | 高め | 低め |
| 必要知識 | 多い | 少ない |
| チーム統一 | しやすい | ルール作りが必要 |
Reduxは、
- store
- reducer
- action
- middleware
など覚える概念が多いです。
その代わり、
「誰が書いても似た構造になる」
という強みがあります。
一方Zustandは、
- 小さく始めやすい
- 試作が速い
という特徴があります。
4 なぜZustandを選ぶのか
ここまでを踏まえると、Zustandを選ぶ理由は主に3つあります。
4-1. コード量が少なく、立ち上がりが速い
Zustandでは、
- action
- reducer
- Provider
などを分けなくてもよいケースが多く、かなり短く書けます。
そのため、
- プロトタイプ
- 個人開発
- 小〜中規模開発
との相性が良いです。
4-2. Reactの外からも扱いやすい
store.getState()
のように、コンポーネント外から状態を扱えます。
これは、
- イベント処理
- 非同期処理
- テスト
- WebSocket
などとの連携で便利です。
4-3. 自由度が高い
Zustandはルールが少ない分、
- チーム独自の設計
- 必要最小限の構造
を作りやすいです。
ただし逆に、
- ルールを決めないとバラつきやすい
という側面もあります。
5 結論:どう使い分けるべきか
整理すると、次のように考えられます。
Zustandが向いている場面
- 小〜中規模アプリ
- 開発スピード重視
- React中心の開発
- シンプルな構造を維持したい
- チーム内で設計ルールを作れる
Reduxが向いている場面
- 大規模アプリ
- 長期運用
- 厳密な状態追跡
- チーム人数が多い
- 統一ルールが重要
おわりに
私はまだReduxとZustandを実務で深く使った経験はありません。
ただ、公式ドキュメントや設計思想を整理していく中で、
- Reduxは「厳密な交通整理」
- Zustandは「最小限で柔軟な管理」
という方向性の違いがかなり見えてきました。
今後は実際に両方を触りながら、
- デバッグ体験
- チーム開発での扱いやすさ
- テストのしやすさ
- 非同期処理との相性
なども比較検証していきたいと思います。