📚

Zustandとは何か?

に公開

はじめに

Next.jsでプロジェクトを作っている時にいろんなファイルで同じ情報とか処理を宣言していることがありました。その時、Zustandっていうツールがその問題を解決してくれると知ったので、今回Zustandが何かを調べてまとめていきます。

結論:複数のコンポーネントで共有したいデータをグローバル変数みたいに一か所で管理して、簡単に利用・更新するためのライブラリ(状態管理)

Zustandとは

軽量・高速・スケーラブル・使い方が非常にシンプルな状態管理ツール。

使い方

ここではカウンターを例に簡単な実装をTypeScriptでしていきます。JavaScriptと書き方がちょっと変わるのでそこはご了承してください。

  1. インストール
npm install zustand
  1. storeを作る
     このstoreというのはhookになります。hookというものはuseState, useEffectのような状態管理などを関数コンポーネントでも使えう仕組みのことです。具体例を見たほうがわかりやすいと思います。
     要は、ここが変数・オブジェクト・関数などをまとめらる場所になります。
// store/counter.ts
import { create } from 'zustand';

interface CounterStore {
    count: number;
    increment: () => void
    decrement: () => void;
    reset: () => void;
}

export const useCounterStore = create<CounterStore>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
    reset: () => set({ count: 0 }),
}));

最初にintefaceをつかってオブジェクトの形を定義しました。これは必須ではないですが、hookはこういう型ですって固定をして型安全性を確保しています。

次に、useCounterStoreというカスタムhookを作りました。いろんなファイルで使うのでexportをつけました。型が無ければ

= create((set) => ({...})

という書き方になります。
setは状態を更新するための関数で、Zustandライブラリが提供しています。
stateはこのストアが持ってる現在の全てのデータのことです。つまり

{
  count: 0,
  increment: ...,
  decrement: ...,
  reset: ...
}

これ全部です。このオブジェクト全体をさしています。ここでのstateはZustandが自動的に渡している現在のstoreの状態です。これが引数として渡していないのに使える理由です。pythonのselfみたいなもんだと思います。

increment: () => set((state) => ({ count: state.count + 1 })),

そしてstate.countはその中のcountプロパティを表しています。

  1. コンポーネントに組み込む
// components/Counter.tsx
import React from 'react';
import { useCounterStore } from '../store/counter';

export default function Counter() {
    const { count, increment, decrement, reset } = useCounterStore();

    return (
        <div className="p-4">
          <h1 className="text-2xl mb-4">Count: {count}</h1>
          <div className="flex gap-2">
            <button onClick={increment} className="px-4 py-2 bg-green-500 rounded">+</button>
            <button onClick={decrement} className="px-4 py-2 bg-red-500 rounded">-</button>
            <button onClick={reset} className="px-4 py-2 bg-gray-500 rounded">Reset</button>
          </div>
        </div>
    );
} 

まずuseCounterStoreをインポートしました。
次にuseCounterStore()を呼び出したのでそれぞれの変数がどうなってるかというと、
count = 0
increment, decrement, resetにはさっき定義した関数が入ってます
そして、buttonが押されると、onClickでそれぞれで設定してる関数が呼び出されます。

まとめ

すごく簡単に状態管理ができるということが分かったかなと思います。
この例では1つのファイルしかないですが、例えば複数ファイルでcountの値を参照・更新したい場合、それぞれのファイル間でデータを無駄にやり取りしたり、それぞれの関数を作る必要があります。
Zustandを使うと、状態と更新関数を1か所にまとめられて、使う時はそれぞれを使いたいファイルでインポートすれば使えます。
自分のプロジェクトでも面倒でしたがZustandに全て移行して、各ファイルのコード量が減って結構すっきりしました。

(参照元:Zustand.docs)

Discussion