42
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Zustand: React向け軽量ステート管理ライブラリ

Last updated at Posted at 2020-08-28

はじめに

本記事では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にも対応しています。ぜひお試しください。

GitHub: https://github.com/react-spring/zustand

42
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?