【備忘録】zustandを使って最速でグローバルステートを実装する

# はじめに
この記事は **zustand** の使い方の **備忘録** です。

## zustandとは
かなり **シンプル** に使える **グローバル状態管理ライブラリ** です。

## インストール
```
npm install zustand
```

## 最速で Global state を定義する
**Global state** は **create関数** に **第1引数** が **set**・**第2引数** が **get** で 
**戻り値** が **オブジェクト** の **callback関数** を渡すことで作成します。

**戻り値のオブジェクト** に **Global state** を **記述** していきます。
オブジェクト内に **state** を **プロパティ** として **記述** し
**更新関数** を **メソッド** として **記述** します。

**更新関数** の中では **set** の引数に **state名と更新したい値** の **オブジェクト** を渡します。

下記が **簡単な実装例** です。

```jsx:app/page.js
"use client";
import { create } from "zustand";

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
}));

export default function Home() {
  const { score, setScore10 } = useStore();

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    </>
  );
}
```

## setの中で現在の state の値を使う
第2引数の **get** を使うことで現在の **state** を取得出来ます。

下記に **実装例** を示します。

```jsx:app/page.js
"use client";
import { create } from "zustand";

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
  scoreIncrement: () => set({ score: get().score + 1 })
}));

export default function Home() {
  const { score, setScore10, scoreIncrement } = useStore();

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    <button onClick={() => scoreIncrement()}>scoreを1増加するボタン</button>
    </>
  );
}
```

**set** の引数に **callback関数** を渡すと第1引数が **現在のstate** になるので **第1引数** を
上手く使うことでも **実装** 出来ます。

下記に **実装例** を示します。

```jsx:app/page.js
"use client";
import { create } from "zustand";

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
  scoreIncrement: () => set({ score: get().score + 1 }),
  scoreDecrement: () => set( (state) => ({ score: state.score - 1 }) )
}));

export default function Home() {
  const { score, setScore10, scoreIncrement, scoreDecrement } = useStore();

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    <button onClick={() => scoreIncrement()}>scoreを1増加するボタン</button>
    <button onClick={() => scoreDecrement()}>scoreを1減少するボタン</button>
    </>
  );
}
```


## Global state から state と 更新関数を取りだす方法
**定義されたGlobal state** から **state と 更新関数** を取り出すには
**1**.**useStore()** の戻り値を **分割代入** する。
**2**.**useStore((state) => state.score))** のように **useStoreの引数にcallback関数** を渡す。
の **2種類** の方法があります。

このうち **2** は実行された時にコンポーネントの再描画が **発生しない** ので
こちらを使う方が**パフォーマンスが良い** です。

**パフォーマンスが良い方法** の実装例を下記に示します。

```jsx:app/page.js
"use client";
import { create } from "zustand";

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
  scoreIncrement: () => set({ score: get().score + 1 }),
  scoreDecrement: () => set( (state) => ({ score: state.score - 1 }) )
}));

export default function Home() {
  const score = useStore((state) => state.score);
  const setScore10 = useStore((state) => state.setScore10);
  const scoreIncrement = useStore((state) => state.scoreIncrement);
  const scoreDecrement = useStore((state) => state.scoreDecrement);

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    <button onClick={() => scoreIncrement()}>scoreを1増加するボタン</button>
    <button onClick={() => scoreDecrement()}>scoreを1減少するボタン</button>
    </>
  );
}
```

**パフォーマンスの良い方法** で **分割代入** する **実装例** を示します。
```jsx:app/page.js
"use client";
import { create } from "zustand";
import { shallow } from 'zustand/shallow';

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
  scoreIncrement: () => set({ score: get().score + 1 }),
  scoreDecrement: () => set( (state) => ({ score: state.score - 1 }) )
}));

export default function Home() {
  const { score, setScore10, scoreIncrement, scoreDecrement } = useStore((state) => ({
    score: state.score,
    setScore10: state.setScore10,
    scoreIncrement: state.scoreIncrement,
    scoreDecrement: state.scoreDecrement
  }), shallow);

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    <button onClick={() => scoreIncrement()}>scoreを1増加するボタン</button>
    <button onClick={() => scoreDecrement()}>scoreを1減少するボタン</button>
    </>
  );
}
```
**useStore** に渡す **callback関数** の戻り値を **オブジェクト** にして
**第2引数** に **shallow** を渡します。

## 追記:更新関数に渡された引数を更新関数の中で使う
入力された値を **コンソールに出力** することで **更新関数に渡された引数** を **更新関数の中** で
使う例を下記に示します。

```jsx:app.page.js
"use client";
import { create } from "zustand";
import { shallow } from 'zustand/shallow';

const useStore = create((set, get) => ({
  score: 0,
  setScore10: () => set({ score: 10 }),
  scoreIncrement: () => set({ score: get().score + 1 }),
  scoreDecrement: () => set( (state) => ({ score: state.score - 1 }) ),
  getArgs: (args) => {
    console.log(args);
  } 
}));

export default function Home() {
  const { score, setScore10, scoreIncrement, scoreDecrement, getArgs } = useStore((state) => ({
    score: state.score,
    setScore10: state.setScore10,
    scoreIncrement: state.scoreIncrement,
    scoreDecrement: state.scoreDecrement,
    getArgs: state.getArgs
  }), shallow);

  return (
    <>
    <h1>scoreの値:{score}</h1>
    <button onClick={() => setScore10()}>scoreを10に変更するボタン</button>
    <button onClick={() => scoreIncrement()}>scoreを1増加するボタン</button>
    <button onClick={() => scoreDecrement()}>scoreを1減少するボタン</button>
    <input onChange={(e) => getArgs(e.target.value)}></input>
    </>
  );
}
```


## さいごに
学習させていただいた先・ライブラリの公式ドキュメントです。

https://reffect.co.jp/react/zustand

https://docs.pmnd.rs/zustand/guides/typescript