ksg

ksg@software_engineer

Portfolio Blog

中学生でもわかるZustand入門 - Reactの「データの共有ノート」をやさしく解説

中学生でもわかるZustand入門 - Reactの「データの共有ノート」をやさしく解説

この記事は何のための記事?

「Zustand(ズースタンド)」というものについて、プログラミングを始めたばかりの人でも分かるように、たとえ話を使って説明します。React(リアクト)というWebサイトやアプリを作るための道具を使っている人向けの話です。

そもそも「状態(state)」って何?

アプリの中には、「今どうなっているか」を覚えておく必要がある情報がたくさんあります。たとえばゲームアプリなら、

  • 今の得点は何点か
  • 今何面(ステージ)にいるか
  • プレイヤーの名前は何か

こういう「今アプリが覚えている情報」のことを、プログラミングでは**「状態(state)」**と呼びます。得点が変わったら画面に表示される数字も変わりますよね。それは「状態が変わったから、画面もそれに合わせて書き換わった」ということです。

困りごと: 遠くの部品にまで情報を届けたい

Reactでは、画面の「ボタン」「文字」「入力欄」など一つ一つの部品をコンポーネントと呼びます。アプリは、たくさんのコンポーネントが親子関係でつながって出来ています。

アプリ全体
├── ヘッダー
│    └── ログインボタン
├── メインエリア
│    └── ショッピングカートのアイコン
└── フッター

もし「ログインした人の名前」を、離れた場所にある「ショッピングカートのアイコン」でも表示したい場合、普通のReactでは、情報を親から子へバケツリレーのように渡していく必要があります。関係のない中間のコンポーネントにも、「その情報を右から左へ受け渡すだけ」の作業をさせなければいけません。

これは、クラスで一番前の席の人が聞いた先生の話を、後ろの席の人に伝えたいときに、間の席の人全員に「そのまま後ろに伝えてね」と頼むようなものです。関係ない人まで巻き込まれて、面倒ですよね。

Zustandは「クラス全員が見られる黒板」

Zustandを使うと、この面倒さから解放されます。

Zustandは、たとえるなら教室の黒板のようなものです。

  • 黒板に書かれた内容は、教室にいる誰でも見ることができる(バケツリレーで伝える必要がない)
  • 誰でも黒板に書き込むことができる
  • 黒板の内容が変わったら、それを見ている人はすぐに気づく

つまり「ログインした人の名前」を黒板(Zustandの保管場所。これを**ストア(store)**と呼びます)に書いておけば、ヘッダーだろうがショッピングカートだろうが、見たい人が直接黒板を見に行けばよいのです。間の人に伝言をお願いする必要がありません。

実際にコードを書いてみる: 得点カウンター

言葉だけだと分かりにくいので、実際に「得点を増やしたり減らしたりするアプリ」を作ってみましょう。

まず、黒板(ストア)を用意します。

import { create } from "zustand";

// 「score」という名前の黒板を作る
const useScoreStore = create((set) => ({
  score: 0, // 最初は0点

  // 「1点増やす」というルールを黒板に書いておく
  addPoint: () => set((state) => ({ score: state.score + 1 })),

  // 「0点に戻す」というルールも書いておく
  reset: () => set({ score: 0 }),
}));

1行ずつ説明します。

  • create(...) は「新しい黒板を作りますよ」という意味です
  • score: 0 は、黒板の左上に「得点: 0」と書いてあるイメージです
  • addPointreset は、「黒板をこう書き換えてね」というボタンのようなものです。この中で使っている set が、実際に黒板の内容を書き換える係です

次に、この黒板を実際に見に行くコンポーネント(画面の部品)を作ります。

function ScoreBoard() {
  // 黒板から「score」の値だけを見に行く
  const score = useScoreStore((state) => state.score);
  // 黒板から「addPoint」という書き換えボタンをもらう
  const addPoint = useScoreStore((state) => state.addPoint);

  return (
    <div>
      <p>今の得点: {score}</p>
      <button onClick={addPoint}>1点追加!</button>
    </div>
  );
}

useScoreStore(...) の部分が「黒板を見に行く」動作です。カッコの中に (state) => state.score と書くことで、「黒板の中の、scoreっていう部分だけ教えて」とお願いしています。これをセレクターと呼びます。

このScoreBoardコンポーネントを、ヘッダーに置いてもフッターに置いても、同じ黒板(useScoreStore)を見ているので、どちらも同じ得点が表示されます。片方でボタンを押して得点を増やせば、もう片方の表示もちゃんと変わります。

なぜ「見たい部分だけ」お願いするの?

さっきのコードで、こう書いていました。

const score = useScoreStore((state) => state.score);

これを、こう書くこともできます。

const state = useScoreStore(); // 黒板全部をまるごと見る

でも、これはあまりおすすめできません。理由はこうです。

黒板には「得点」以外にも、いろいろな情報が書かれているとします(例えば「プレイヤーの名前」なども)。もし黒板を「まるごと」見ている場合、プレイヤーの名前がちょっと変わっただけでも、Reactは「もしかしたら画面の見た目を変える必要があるかも」と思って、そのコンポーネントを最初から書き直そう(これを再レンダリングと呼びます)とします。得点の表示には関係ないのに、です。

これは、黒板の隅っこにある「今日の給食メニュー」が書き換わるたびに、得点しか見ていない人まで「あ、黒板変わった!確認しなきゃ」と毎回振り向いてしまうようなものです。無駄ですよね。

なので、「自分が本当に必要な部分だけを教えて」とセレクターでお願いすることで、関係ない書き換えでは反応しないようにできます。これがZustandを使う上で一番大事なコツです。

サーバーからデータをもらう場合(非同期処理)

アプリによっては、自分のスマホやパソコンの中だけでなく、インターネットの向こう側にあるサーバーという別のコンピューターに「データちょうだい」とお願いして、返事を待つことがあります。友達にLINEで「今日の宿題何だっけ?」と聞いて、返事が来るまで少し待つのと似ています。

Zustandでは、これも特別な準備なしに、黒板のルールの中に書くことができます。

const useTodoStore = create((set) => ({
  todos: [],       // やることリスト(最初は空っぽ)
  isLoading: false, // 今読み込み中かどうか

  fetchTodos: async () => {
    set({ isLoading: true }); // 「読み込み中」と黒板に書く

    const response = await fetch("/api/todos"); // サーバーにお願いして、返事を待つ
    const todos = await response.json();        // 返事の中身を取り出す

    set({ todos: todos, isLoading: false }); // 黒板に「もらったリスト」を書いて、読み込み中は終わりにする
  },
}));

await というのは「ここで返事が来るまで待つよ」という意味の言葉です。返事が来たら、次の行に進みます。

Zustandを使わないとどうなる?(比較)

Reactには他にも似たような道具(Context APIやRedux)がありますが、Zustandは特に「準備が少なくて済む」のが特徴です。

Zustand

Reactの標準機能(Context)

黒板を使うための下準備

ほぼ不要

アプリ全体を専用の枠で囲む必要がある

書き方の量

少ない

やや多い

関係ない書き換えで無駄に反応する

セレクターを使えば防げる

気をつけないと防ぎにくい

つまり「とりあえず簡単にアプリ全体で共有できるデータを作りたい」というときに、Zustandはとても手軽な選択肢です。

まとめ

  • **状態(state)**とは、アプリが今覚えている情報のこと(得点、名前など)
  • Zustandは、教室の黒板のように、アプリ中のどこからでも見たり書いたりできる共有の保管場所(ストア)を作れる道具
  • 黒板を見に行くときは、**「自分が必要な部分だけ」**を指定する(セレクター)と、無駄な書き直しを防げる
  • サーバーからデータをもらうような処理も、特別な準備なしにそのまま書ける

一度自分の手で小さな黒板(ストア)を1つ作ってみると、感覚がつかみやすいと思います。まずは今回の「得点カウンター」を真似して動かしてみるのがおすすめです。

おすすめの記事