Redux のボイラープレートに別れを?軽量な状態管理ライブラリ Zustand 詳解

5 分

1. Redux の「悩み」

Redux が React エコシステムで最も有名かつ強力な状態管理ライブラリであることに疑いはありません。単方向のデータフローやタイムトラベルデバッグなどの特性により、大規模で複雑なアプリケーションでは信頼できる選択肢です。しかし、多くの小・中規模プロジェクトにとって、Redux の「ボイラープレート(Boilerplate)」は悩みの種になりがちです。

  • Actions & Action Creators: 大量の action の型と作成関数を定義します。
  • Reducers: 異なる action を処理するため、大規模な switch 文を記述します。
  • Dispatch & Selectors: コンポーネント内で dispatch によって action を送り、useSelector で状態の変化を購読します。
  • Context Provider: アプリケーションのルートを <Provider> で包む必要があります。

そのため、単純な状態を一つ追加するだけでも複数のファイルを変更する必要があり、手順が煩雑になります。

2. Zustand:新鮮な選択肢

Zustand(ドイツ語で「状態」)は、react-three-fiber の作者である Poimandres チームが開発した軽量な状態管理ライブラリです。その設計思想はミニマル非侵襲的であることです。

中核的な特徴:

  • コード量が非常に少ない: ある機能を実装するとき、Zustand のコード量は通常 Redux のごく一部です。
  • Hooks ベース: すべてが 1 つのカスタム Hook を中心に構成されており、現代的な React 開発の習慣に沿っています。
  • Context Provider が不要: アプリケーションの上層を Provider で包む必要はありません。Store はコンポーネントツリーから独立し、どこからでもインポートして利用できます。
  • 習得が容易: API が非常にシンプルで、中核的な使い方を数分で学べます。

3. 中核的な使い方

Zustand の利用は、Store の作成コンポーネントでの利用という 2 つの手順に分かれます。

a. Store を作成する

store はどの .js または .ts ファイルにも定義できます。

// src/store.js
import { create } from 'zustand';

const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

export default useBearStore;

create 関数はコールバックを受け取り、その引数が set 関数(React の setState に似た関数)です。ここで状態と、状態を更新するメソッドを定義します。

b. コンポーネントで利用する

どのコンポーネントでも、通常の Hook と同じように使えます。

// src/components/BearCounter.jsx
import useBearStore from '../store';

function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} around here ...</h1>;
}

セレクタ関数 (state) => state.bears を使い、bears の状態を購読している点に注目してください。これにより、コンポーネントは bears が変化したときだけ再レンダリングされるため、不要なパフォーマンスコストを避けられます。

// src/components/Controls.jsx
import useBearStore from '../store';

function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation);
  return <button onClick={increasePopulation}>one up</button>;
}

action の取得も同じように簡単です。

4. 非同期 Action

Zustand は非同期処理も非常に自然に扱えます。redux-thunkredux-saga のようなミドルウェアは必要ありません。

const useAsyncStore = create((set) => ({
  data: null,
  fetchData: async (url) => {
    const response = await fetch(url);
    const data = await response.json();
    set({ data });
  },
}));

まとめ

Zustand は Redux を完全に置き換えようとするものではありません。厳格な規約、追跡可能性、巨大なエコシステムという点では、Redux に今も優位性があります。

しかし、大多数の React アプリケーションにとって、Zustand はよりシンプルかつ高速で、認知的な負担が少ない選択肢を提供します。Redux の煩雑な手続きに疲れている場合や、次のプロジェクトで軽快かつ柔軟な状態管理ライブラリが必要な場合、Zustand は間違いなく試す価値のある優れた選択肢です。