🐻

Next.js で React状態管理ライブラリの Zustand を使ってみる

2023/06/10に公開

Reactの状態管理ライブラリはいくつか存在します。
今回はそのうちの1つの「Zustand」を紹介していきたいと思います。

Reactにおける状態管理とは

まず冒頭出た、Reactの状態管理ってなんなの?という話です。
私はめっぽう説明が下手くそなので、某先生を呼んできました。

ChatGPTせんせー
ChatGPT先生にお礼は言いましょうね。

このような回答が返ってきました。
先生のおっしゃる通りで、Reactには2種類の状態管理があり、
ローカルな状態管理の代表例として、useStateがあります。
https://ja.legacy.reactjs.org/docs/hooks-state.html

対してグローバルな状態管理の代表例として、Reduxが挙げられます。
https://redux.js.org/

Zustandは後者のグローバルな状態管理ライブラリとして利用されます。
つまり、Reduxは競合にあたります。

先生も言及されていますが、このような状態管理のライブラリは非常に便利な半面、
規模の大きいプロジェクトになればなるほど肥大化し、結果として扱いが困難になってしまう可能性がありますので、
適切なルールに基づいた管理が求められると言えます。

主な状態管理ライブラリ

前述の Redux 以外にも Recoil、Jotai など、React の状態管理ライブラリは多く存在します。
状態管理一つにしても、これだけの量のライブラリがあるということは驚きでした。

状態管理ライブラリについては、↓こちらの記事がすごく参考になりました。
https://zenn.dev/kazukix/articles/react-state-management-libraries

Zustandについて

本題の Zustand についてです。
トレンドには乗ってきており、伸びているのがわかりますね。
https://github.com/pmndrs/zustand

https://npmtrends.com/jotai-vs-mobx-vs-nanostores-vs-recoil-vs-redux-vs-valtio-vs-zustand

使ってみる

実際にTodoアプリケーションで作ってみる。

Storeの作成

todo をため込む配列、todo を追加する addTodo、todo を削除する removeTodo を用意しました。
todo の状態を常に Store が管理してくれます。

store の書き方はとても簡単なので、Guthub を見ながらやってみてください。

TodoStore.ts
import { create, useStore } from "zustand";

type TodoStore = {
    todos: string[];
    addTodo: (todo: string) => void;
    removeTodo: (todo: string) => void;
};

export const useTodoStore = create<TodoStore>((set) => ({
    todos: [],
    addTodo: (todo) => set((state) => ({ todos: [...state.todos, todo] })),
    removeTodo: (todo) => set((state) => ({ todos: state.todos.filter((t) => t !== todo) })),
}));

雑にひな型を作成

アイコンは Fontawesome、コンポーネントは mantine を使いました。
※めちゃくちゃ雑なのは無視で・・

index.tsx
const TodoLists = () => {
  const todos = [
  "ごみ捨て",
  "洗濯",
  "買い物"
  ];

  const rows = todos.map((todo) => (
    <tr key={todo}>
      <td>{todo}</td>
      <td><Button leftIcon={<FontAwesomeIcon icon={faTrash}/>}>削除する</Button></td>
    </tr>
  ));

  return (
    <Table sx={{width: 500}}>
      <thead>
        <tr>
          <th>Todoリスト</th>
          <th>削除</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </Table>
  );
}

const Page = () => {
  const form = useForm({ initialValues: { todo: "" } });
  return (
    <Box>
      <Title >Todoリスト</Title>
      <Box sx={{display: "flex", marginBottom: 50}}>
      <TextInput onChange={(e) => form.setFieldValue("todo", e.target.value)}></TextInput>
      <Button>TODOにする</Button>
      </Box>
      <TodoLists/>
    </Box>
  )
}

こんな感じで、お洒落なTodoリストが出来上がりました。
Store は適用していないので全く動きません。
todo

Storeを適用する

前述した、todo、addTodo、removeTodo を使って以下のように記述していきます。

index.tsx
const TodoLists = () => {
  const {todos, removeTodo} = useTodoStore();

  const rows = todos.map((todo) => (
    <tr key={todo}>
      <td>{todo}</td>
      <td><Button onClick={() => removeTodo(todo)} leftIcon={<FontAwesomeIcon icon={faTrash}/>}>削除する</Button></td>
    </tr>
  ));

  return (
    <Table sx={{width: 500}}>
      <thead>
        <tr>
          <th>Todoリスト</th>
          <th>削除</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </Table>
  );
}

const Page = () => {
  const {addTodo} = useTodoStore();
  const form = useForm({ initialValues: { todo: "" } });
  return (
    <Box>
      <Title >Todoリスト</Title>
      <Box sx={{display: "flex", marginBottom: 50}}>
      <TextInput onChange={(e) => form.setFieldValue("todo", e.target.value)}></TextInput>
      <Button onClick={() => addTodo(form.values.todo)}>TODOにする</Button>
      </Box>
      <TodoLists/>
    </Box>
  )
}

ここまで完了すると以下のように Todo が作成されます。
当然、コンポーネントでStoreを管理しているわけではないので、別のコンポーネントだったり、ページだったりで呼び出すことも可能です。非常に便利。
todo

おわりに

チョー簡単に Zustand 解説、やってみた系してみました。
皆さんもぜひ、Zustand 使って、Reactをどんどん便利にしていきましょう!(ただし、使うのであれば厳重に管理すること!)

では。

コラボスタイル Developers

Discussion