第174章:アクション(関数)でストアを更新する
Zustandの「ストア」って、みんなで共有する“データ置き場”でしたよね? この章では、そのデータを**安全&キレイに更新するための「アクション(関数)」**を作ります😊🛠️
この章のゴール 🎯
- ストアの中に「更新用の関数(アクション)」を用意できる ✅
setを使って 安全に state を更新できる ✅- Reactコンポーネントから アクションを呼んで更新できる ✅
アクションってなに?🤔➡️💡
**アクション = ストアの値を更新するための“専用ボタン(関数)”**です🎮✨ コンポーネント側でゴチャゴチャ更新ロジックを書くより、ストアにまとめるとスッキリします💅
図でイメージしよう 🗺️
set の更新方法は2種類あるよ ✌️✨
① そのまま上書き(単純な更新)🧼
set({ count: 0 }) みたいに、部分的に上書きできます。
② 関数で更新(前の state を使う)🧠⚡
set((state) => ({ count: state.count + 1 }))
前の値に依存する更新(+1 とか)はこっちが基本です🙆♀️
実装してみよう:カウンターストア(アクション付き)🔢🐻
1) src/stores/counterStore.ts を作る ✍️
import { create } from "zustand";
type CounterStore = {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
setCount: (value: number) => void;
incrementBy: (delta: number) => void;
};
export const useCounterStore = create<CounterStore>((set) => ({
count: 0,
// ✅ 前の値に +1 するので「関数更新」
increment: () => set((state) => ({ count: state.count + 1 })),
// ✅ -1 も同じ
decrement: () => set((state) => ({ count: state.count - 1 })),
// ✅ 固定値にするなら「上書き」でもOK
reset: () => set({ count: 0 }),
// ✅ 任意の値を入れる(上書き)
setCount: (value) => set({ count: value }),
// ✅ まとめて増やす(前の値を使う)
incrementBy: (delta) => set((state) => ({ count: state.count + delta })),
}));
コンポーネントから使う(アクションを呼ぶ)🧩🎉
2) src/components/Counter.tsx
import { useState } from "react";
import { useCounterStore } from "../stores/counterStore";
export function Counter() {
const count = useCounterStore((s) => s.count);
const increment = useCounterStore((s) => s.increment);
const decrement = useCounterStore((s) => s.decrement);
const reset = useCounterStore((s) => s.reset);
const setCount = useCounterStore((s) => s.setCount);
const incrementBy = useCounterStore((s) => s.incrementBy);
const [input, setInput] = useState("0");
const [delta, setDelta] = useState("5");
return (
<div style={{ padding: 16 }}>
<h2>カウンター: {count} 🧸</h2>
<div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
<button onClick={decrement}>-1 ➖</button>
<button onClick={increment}>+1 ➕</button>
<button onClick={reset}>Reset 🔁</button>
</div>
<div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 12 }}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
style={{ width: 120 }}
/>
<button
onClick={() => setCount(Number(input))}
>
この値にする ✨
</button>
</div>
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
<input
value={delta}
onChange={(e) => setDelta(e.target.value)}
style={{ width: 120 }}
/>
<button onClick={() => incrementBy(Number(delta))}>
まとめて足す 🚀
</button>
</div>
</div>
);
}
3) App.tsx で表示
import { Counter } from "./components/Counter";
export default function App() {
return <Counter />;
}
よくあるミスあるある 😵💫💥(回避しよ!)
❌ ミス1:ストアの state を直接いじる(やっちゃダメ)🙅♀️
Zustandでは、更新は set 経由でやるのがルールです✅
❌ ミス2:set({ count: state.count + 1 }) みたいに書こうとする
その state どこの?ってなるやつです😂
前の値を使うならこう👇
set((state) => ({ count: state.count + 1 }));
✅ コツ:更新ロジックは「ストア側」に寄せる 🧠🏡
コンポーネントがスッキリして、あとで直すのも楽になります🎀
ミニ演習(手を動かすと最強)✍️🔥
演習A:decrementBy(delta) を追加しよ ➖➖
- ストアに
decrementBy: (delta: number) => voidを追加 - 実装は
set((s) => ({ count: s.count - delta }))
演習B:double() を追加しよ ✨×2
double: () => void- 実装は
set((s) => ({ count: s.count * 2 }))
まとめ 🧸✅
- **アクションは「更新専用の関数」**で、ストア内に置くとキレイ✨
- 前の値を使う更新は
set((state) => ...)が基本🧠 - コンポーネントは アクションを呼ぶだけにすると気持ちいい🎉
次の章(第175章)では、さらに気持ちよくする **「セレクター」**で「必要なときだけ再レンダリング」へ進みます🐻⚡