概要
Zustand Test は 4 つの store を持つため、devtools の名前と action 名を雑に書くと 「タイムラインに setState が並ぶだけ」 で何が起きたか追えなくなります。
本記事では、Playground で採用している命名規則を 4 つに分けて整理します。
| 場所 | 何を書くか |
|---|---|
devtools(..., { name: "..." }) | DevTools 上での store 識別名 |
set(values, false, "actionName") | 1 つ 1 つの アクション名 |
set(initial, true, "reset") | replace モード(merge ではなく置き換え) |
| 既存 action の 派生・補助 | setX:byY のように 1 トークンに圧縮 |
インストール
npm install zustand
Chrome 拡張の Redux DevTools Extension もインストールしておきます(DevTools の Redux タブから開きます)。
store 名は PascalCase で揃える
DevTools は複数の store を タブで切り替える UI で表示します。名前を揃えておくと並んだときに読みやすくなります。
import { create } from "zustand";
import { devtools } from "zustand/middleware";
export const useSettingsStore = create<SettingsState>()(
devtools((set) => ({ /* ... */ }), { name: "SettingsStore" })
);
export const useProfileStore = create<ProfileState>()(
devtools((set) => ({ /* ... */ }), { name: "ProfileStore" })
);
export const useDetailStore = create<DetailState>()(
devtools((set) => ({ /* ... */ }), { name: "DetailStore" })
);
export const useUiStore = create<UiState>()(
devtools((set) => ({ /* ... */ }), { name: "UiStore" })
);
useFooStore の Foo 部分を FooStore として揃える だけで、DevTools での識別が一気に楽になります。
アクション名は 動詞 + 対象 を基本にする
set の第 3 引数にアクション名を渡すと、タイムラインに表示されます。命名規則を作っておくと、流れがそのまま読めます。
patchFormData: (partial) => set(partial, false, "patchFormData"),
setFormData: (values) => set(values, false, "setFormData"),
reset: () => set(INITIAL_SETTINGS, true, "reset"),
推奨は次のとおり。
- 基本:
動詞 + 対象(setFormData/setSuggestedRate/setTab) - 部分更新:
patch + 対象(patchFormData) - 派生・条件付き:
動詞 + 対象 + コロン + 修飾(setSuggestedRate:fromSettingsのように) - リセット:
resetだけで OK。store 名で識別が付く
storeSync 経由で書き込まれた action は 由来を明示 すると追いやすくなります。
useDetailStore.getState().setSuggestedRate(next);
// ↓ detailStore 側
setSuggestedRate: (rate) => set({ suggestedRate: rate }, false, "setSuggestedRate:fromSettings"),
ただし、複数の由来を区別する必要が無いなら コロン修飾は付けない方がノイズが少ない です。Playground では suggestedRate だけ修飾を付けています。
replace モードを使い分ける
set の 第 2 引数 が replace フラグです。true を渡すと merge ではなく 置き換え になります。
| 第2引数 | 挙動 | 使う場面 |
|---|---|---|
false (デフォルト) | 部分マージ | setFormData / patchFormData / 通常更新 |
true | 全置き換え | reset / 初期化など state 形が変わるとき |
reset: () => set(INITIAL_SETTINGS, true, "reset"),
reset を merge にしてしまうと、reset 後に古いキーが残るリスクがあります。state 形を初期値そのものに戻したい ときは必ず true を渡しましょう。
派生 store の整合を保つ書き込み
storeSync のリスナーから set するアクションは「由来は別 store の変化 だ」とアクション名で示します。これにより、DevTools のタイムラインで「どの store の変化が連鎖を起こしたか」を追跡できます。
// settings → detail(suggestedRate)
useSettingsStore.subscribe(
(s) => s.rate,
(rate) => {
const next = Math.max(0, rate * 1.1);
useDetailStore.getState().setSuggestedRate(next);
}
);
タイムライン上の見え方:
SettingsStore patchFormData { rate: 50 }
DetailStore setSuggestedRate { suggestedRate: 55 }
連鎖が 2 行で読める ように命名しておくのが目標です。
タイムトラベルで再現するときの注意
DevTools の「Jump」(時間旅行)は state を その時点に戻すだけ で、subscribe のリスナーは再実行しません。
- ✅ できる: 過去の state を表示状態として再現する
- ❌ できない: 過去の state から
storeSyncを 再シミュレーション する
挙動を再現したいときは、UI 操作をやり直す(reset から始め直す)方が確実です。Playground に「全ストアをリセット」ボタンを置いているのは、DevTools での検証フローを楽にする目的もあります。
ポイント
- store 名は
<Domain>Storeの PascalCase で統一する。DevTools のタブが揃って読みやすくなる - アクション名は
動詞 + 対象を基本に、patch/reset/:from修飾を補助で使う setの第 2 引数はresetでだけtrue(replace モード)にする。state 形を初期値に戻すためstoreSync経由の書き込みは由来をアクション名で示す。DevTools 上で 連鎖が 2 行で読める よう設計する- DevTools のタイムトラベルは
subscribeを再実行しない。再現性が要るならリセットからやり直す
関連記事
セットで読むと文脈がつながる関連記事は次のとおりです。
- Zustand 編 — devtools と subscribeWithSelector を併用する場合の middleware 順序
- storeSync テスト編 — タイムラインの代わりにテストで連鎖を固定する
- アクション受け渡しパターン編 — selector で取り出すアクションの参照安定性
- Zustand Test (Build) — 完成例と外部デモ