Zustand:Reduxという重石を捨てて軽やかに走る
Reduxでユーザー名を一つ更新するためだけに、何十行ものボイラープレートを書いて「疲れ果てた」経験はありませんか?ESLintやPrettierでコードを整えるのと同じように、ステート管理もシンプルに保つことが重要です。もしそうなら、Zustandが救世主になります。ミニマリズムを追求したこのライブラリは、サイズがわずか1.1KB。アプリをProviderでラップする必要も、actions.jsやreducers.jsといった複雑な構成も不要です。
一瞬でインストール完了:
npm install zustand
# または
yarn add zustand
試しにカウンター管理用のストアを作成してみましょう。すぐにその違いを実感できるはずです:
import { create } from 'zustand'
const useCounterStore = create((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}))
function Counter() {
const { count, inc, dec } = useCounterStore()
return (
<div>
<h1>{count}</h1>
<button onClick={inc}>増やす</button>
<button onClick={dec}>減らす</button>
</div>
)
}
最大のメリットは?フックをインポートしてそのまま使うだけ。ルートファイルで面倒な<Provider>の設定をする必要はありません。
実務プロジェクトでZustandを選ぶべき理由
以前参加した複雑なダッシュボードのプロジェクトでは、Context APIの使用がパフォーマンスのボトルネックになっていました。ユーザーが入力欄に一文字打つたびに、階層下にある大量のチャートが無駄に再レンダリングされていたのです。Zustandに切り替えたところ、スマートなレンダリング機構のおかげでUIのレスポンス速度が約30%向上しました。
Zustandは3つの大きな問題を根本的に解決します:
- レンダリングの制御: 実際にデータが変更されたコンポーネントのみが再レンダリングされます。
- API hiện đại: React Hooksのパワーを最大限に活用しており、開発者に非常に親切です。
- 柔軟なアクセス: フックを使わずに、API設定ファイルやユーティリティ関数内から直接ステートを取得することも可能です。
実戦における主要機能
1. 非同期アクション(Async Actions)の処理が驚くほど簡単
Redux-Thunkのようなミドルウェアのことは忘れてください。Zustandなら、通常のJavaScript関数のようにストア内でasync/awaitを直接使えます。効率的なRESTful APIを構築したバックエンドからデータを取得する際も、コードを非常にシンプルに保てます。
const useUserStore = create((set) => ({
users: [],
loading: false,
fetchUsers: async () => {
set({ loading: true })
try {
const res = await fetch('https://api.example.com/users')
const data = await res.json()
set({ users: data, loading: false })
} catch (error) {
set({ loading: false })
console.error("データ読み込みエラー:", error)
}
},
}))
2. Persistミドルウェア – ステートの自動保存
カート情報やテーマ設定をlocalStorageに保存する機能は、通常多くのロジックを必要としますが、Zustandならラップ関数を一つ使うだけで解決します。
import { persist } from 'zustand/middleware'
const useSettingsStore = create(
persist(
(set) => ({
theme: 'dark',
toggleTheme: () => set((s) => ({ theme: s.theme === 'dark' ? 'light' : 'dark' })),
}),
{ name: 'user-preferences' }
)
)
パフォーマンス最適化:ストアを「丸ごと」取り出さない
初心者がよくやってしまうミスに、const state = useStore()のようにストア全体を取得することがあります。これではストア内のどれか一つが変わるたびにコンポーネントが再レンダリングされてしまいます。最適化のためにSelectorを使用しましょう:
// countが変更された時のみ再レンダリング
const count = useCounterStore((state) => state.count)
const inc = useCounterStore((state) => state.inc)
このアプローチにより、ストアが肥大化してもアプリケーションの動作を滑らかに保つことができます。
プロジェクト規模拡大に伴うストア構成のベストプラクティス
Slicesパターンの適用
すべてのロジックを一つのファイルに詰め込まないでください。ストアを「Slice」と呼ばれる単位(例:AuthSlice、CartSlice)に分割し、それらを統合します。この方法により、コードの可読性と保守性が向上し、効果的なコードレビューの際もロジックの理解がスムーズになります。
Redux DevToolsの活用
ZustandはRedux DevToolsと非常に相性が良いです。Reduxを使っている時と同じようにデバッグやステートの変更履歴の確認ができますが、開発者向け無料オンラインツールなどと併せて活用することで、作業効率はさらに向上します。
ストアを使わない時を見極める
使いすぎには注意しましょう。特定のコンポーネント内だけで完結する状態(例:単一のモーダルの開閉状態など)であれば、React標準のuseStateが最適です。複数の画面で共有する必要があるデータや、永続化が必要なデータのみをストアに配置しましょう。
終わりに
Zustandは単なるライブラリではなく、実用的かつモダンなステート管理の考え方そのものです。冗長な規約よりも、開発スピードと実際のパフォーマンスを優先します。新しいReactプロジェクトや、Next.jsでの開発を始めるなら、ぜひZustandを試してみてください。きっと後悔しないはずです。
ハッピーコーディング!

