Next.js で React状態管理ライブラリの Zustand を使ってみる
Reactの状態管理ライブラリはいくつか存在します。
今回はそのうちの1つの「Zustand」を紹介していきたいと思います。
Reactにおける状態管理とは
まず冒頭出た、Reactの状態管理ってなんなの?という話です。
私はめっぽう説明が下手くそなので、某先生を呼んできました。

※ChatGPT先生にお礼は言いましょうね。
このような回答が返ってきました。
先生のおっしゃる通りで、Reactには2種類の状態管理があり、
ローカルな状態管理の代表例として、useStateがあります。
対してグローバルな状態管理の代表例として、Reduxが挙げられます。
Zustandは後者のグローバルな状態管理ライブラリとして利用されます。
つまり、Reduxは競合にあたります。
先生も言及されていますが、このような状態管理のライブラリは非常に便利な半面、
規模の大きいプロジェクトになればなるほど肥大化し、結果として扱いが困難になってしまう可能性がありますので、
適切なルールに基づいた管理が求められると言えます。
主な状態管理ライブラリ
前述の Redux 以外にも Recoil、Jotai など、React の状態管理ライブラリは多く存在します。
状態管理一つにしても、これだけの量のライブラリがあるということは驚きでした。
状態管理ライブラリについては、↓こちらの記事がすごく参考になりました。
Zustandについて
本題の Zustand についてです。
トレンドには乗ってきており、伸びているのがわかりますね。
使ってみる
実際にTodoアプリケーションで作ってみる。
Storeの作成
todo をため込む配列、todo を追加する addTodo、todo を削除する removeTodo を用意しました。
todo の状態を常に Store が管理してくれます。
store の書き方はとても簡単なので、Guthub を見ながらやってみてください。
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 を使いました。
※めちゃくちゃ雑なのは無視で・・
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 は適用していないので全く動きません。

Storeを適用する
前述した、todo、addTodo、removeTodo を使って以下のように記述していきます。
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を管理しているわけではないので、別のコンポーネントだったり、ページだったりで呼び出すことも可能です。非常に便利。

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