はじめに
本記事ではZustandという軽量ステート管理ライブラリを紹介します。ZustandはFlux/Reduxの流れを汲むステート管理ライブラリです。React向けに必要な機能だけに削ぎ落とし、シンプルなAPIを提供していることが特徴です。また、Contextを用いていないことも特徴の一つです。今回は技術的な側面よりも、基本的な使い方を紹介します。
インストール
npm install zustand
Zustandは「チュースタンド」のように読み、ドイツ語で「状態」を意味します。
追記2022/7/29: 私は最近はドイツ語読みはやめて、英語読みの「ザスタンド」か「ズゥスタンド」にしています。
一番簡単な例:「カウンター」
import React from 'react';
import create from 'zustand';
// ステートの型を定義する、アクションもステートの一部
interface State {
count: number;
increment: () => void;
}
// ステートの実装からhookを作る
const useStore = create<State>((set) => {
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
});
// コンポーネントでhookを使う
const App: React.FC = () => {
const { count, increment } = useStore();
return (
<div>
{count} <button onClick={increment}>+1</buttoon>
</div>
);
};
上記例では、useStore
はAppコンポーネントでしか使っていませんが、実際は複数のコンポーネントで何度でも使うことができます。
セレクターによる限定的な読み込み
React-ReduxのuseSelectorに慣れている方にはすぐ伝わると思いますが、セレクターを指定することができます。
const Action: React.FC = () => {
const increment = useStore((state) => state.increment);
return (
<div>
<button onClick={increment}>Increment count</buttoon>
</div>
);
};
この場合increment
は通常変化しないため、このAction
コンポーネントはcount
が変化してもre-renderしません。
storeに値を複数入れてそれを個別に読み込むこともできます。
const useStore = create<State>((set) => {
count1: 0,
count2: 0,
increment1: () => set((state) => ({ count1: state.count1 + 1 })),
increment2: () => set((state) => ({ count2: state.count2 + 1 })),
});
ちょっと複雑なアクション
アクションは複数の値を同時に変更することができます。アクションの作り方に制約はなく柔軟に作ることができます。
const useStore = create<State>((set) => {
count1: 0,
count2: 0,
multiplyCounts: (by) => set((state) => ({
count1: state.count1 * by,
count2: state.count2 * by,
})),
});
非同期のアクションを作ることもできます。
const useStore = create<State>((set) => {
count: 0,
fetchCount: async () => {
const res = await fetch(...);
const data = await res.json();
set({ count: data.count });
},
});
この例は、set
に更新関数ではなく更新するオブジェクトを直接指定しています。
アクション内の処理でステートにアクセスしたい場合は第二引数のget
を使うことができます。
const useStore = create<State>((set, get) => {
id: 'foo'
count: 0,
fetchCount: async () => {
const res = await fetch('http://.../id=' + get().id);
const data = await res.json();
set({ count: data.count });
},
});
おわりに
以上が基本機能の紹介でした。基本と言っても、Reactで使う範囲では、この応用でおおよそのことが実現可能です。また、immerのとの組み合わせはとても強力ですし、Redux DevToolsにも対応しています。ぜひお試しください。