React状態管理ライブラリ Zustandを使ってみた

2022.07.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。

Zustandとは

ZustandはReactの状態管理ライブラリの一つです。シンプルで使いやすく、他の状態管理ライブラリと比べてもコード量が少ないです。

ちなみに同じ状態管理ライブラリ、jotaiやvaltioもZustandと同じPoimandresが提供しています。 valtioに関しては、こちらのエントリで記述しています。

試してみた

Next.jsを使用して、プロジェクトを作成します。

$ npx create-next-app sample-zustand --ts

プロジェクトに移動し、Zustandをインストールします。

$ yarn add zustand

// または
$ npm install zustand

useStore

Zustandでは、状態を管理するuseStoreフックを作成します。create関数内で定義していき、set関数を使用して状態を変更します。

/libs/store.tsを作成し、以下を記述します。

/libs/store.ts

import create from "zustand";

type Count = {
  count: number;
  increaseCount: () => void;
  resetCount: () => void;
}

export const useStore = create<Count>((set) => ({
  count: 0,
  increaseCount: () => set((state) => {
    return { count: state.count + 1 }
  }),
  resetCount: () => set({ count: 0 }),
}));

index.tsxを変更し、useStore()を呼び出します。

/pages/index.tsx

import type { NextPage } from "next";
import { useStore } from "../libs/store";
import styles from "../styles/Home.module.css";

const Count = () => {
  const count = useStore((state) => state.count);
  return (
    <>
      <h2>count: {count}</h2>
    </>
  );
};

const IncreaseCount = () => {
  const increaseCount = useStore((state) => state.increaseCount);
  return (
    <>
      <button onClick={() => increaseCount()}>Increase</button>
    </>
  );
};

const ResetButton = () => {
  const { resetCount } = useStore();

  return <button onClick={() => resetCount()}>Reset</button>;
};

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <Count />
        <IncreaseCount />
        <ResetButton />
      </main>
    </div>
  );
};

export default Home;

ローカル環境で、動作確認します。

$ yarn dev

useStore()で全てのデータを呼び出すことができますが、変更があるたびに再レンダリングされるので、基本的には取得したいstateだけを指定します。

const IncreaseCount = () => {
  const state = useStore();

    // 毎回表示される
  console.log(state);
  return (
    <>
      <button onClick={() => state.increaseCount()}>Increase</button>
    </>
  );
};

countが増えるタイミングで、再度ログが表示されています。

const IncreaseCount = () => {
  const increaseCount = useStore((state) => state.increaseCount);

  // 初回のみ表示され、以降はincreaseCountに変更があるまで表示されない
  console.log("render")
  return (
    <>
      <button onClick={() => increaseCount()}>Increase</button>
    </>
  );
};

初回のみレンダリングされます。

shallow

複数の値を指定する場合は第二引数にshallowを渡すことで、状態の更新がshallowイコールで(第一階層のみで)比較されるようになり、レンダリング回数を減らすことができます。

import shallow from "zustand/shallow";

...

const Home: NextPage = () => {
  const { count, increaseCount } = useStore((state) => ({
    count: state.count,
    increaseCount: state.increaseCount,
  }), shallow);

  return (
     ...
  );
};

overWrite

使い所は難しいですが、状態そのものを置き換えることもできます。この場合、定義した状態がundefinedになるので、依存関係を気にする必要があります。

export const useStore = create<Count>((set) => ({
  count: 0,
  increaseCount: () =>
    set((state) => {
      return { count: state.count + 1 };
    }),
  resetCount: () => set({ count: 0 }),
  overWrite: () => set({}, true),
}));

以下はcountの値を表示し、overWriteを実行しています。

まとめ

今回は、状態管理ライブラリのZustandを触ってみました。Reactの状態管理ライブラリも、選択肢の幅が増えてきています。そのライブラリの特徴を把握し、プロジェクトによって使い分けれるようにこれからも勉強を続けていきます。

ではまた。