🐻

【React状態管理】zustandが簡単で書きやすい

2022/09/20に公開

コンテキストの課題

Providerタワーが積み重なってきたな・・・」
「このままグローバルstate管理をコンテキストに一任するのはまずい・・・」
「コンテキストに一任するとこんなデメリットがあるよね」

コンテキストのデメリット💣

  • 依存していないstate更新によるコンポーネントの不要な再レンダリング
  • 不要な際レンダリングを防止するためのメモ化のコスト増加
  • Provider同士の依存による開発者の負荷増進

「コンテキストは必要に応じて使うべきで、」
「グローバルstate管理をしたいんだったら状態管理ライブラリを使った方がいいよね」

状態管理ライブラリ

「調べるとたくさんあるな」
「Reduxが一番有名だけど、」
「最近はMetaが開発してるRecoilなるものもあるらしい」
「でもまだバージョン1に満たないからプロジェクトには提案しにくいな・・・」
npm trendsでみるとmobxていうのもあるんだ」
「このライブラリは古から使われているんだな」
「クラス記法で特殊っぽいし一旦置いておこう」
zustandってライブラリ、Githubのスター数もそこそこあるし、成熟してる感もある」
「日本人が開発に携わってるんだ!」
「マスコットのくまもなんかかわいいし使ってみよう」

zustand:Github

zustandでstoreの作成

Zustand Documentation

  • storeの作成にはcreateを使う
  • createset, getを引数に取る
  • setはstateの更新
  • getはstateの取得
  • 非同期処理もstore内部で簡単に書ける
import create from 'zustand'

const useStore = create((set, get) => ({
  editedTodo: {
    id: 0,
    title: '',
  }
  todos: [],
  fetchTodos: async () => {
    const res = await fetchTodos()
    set({ todos: res.todos })
  },
  createTodo: () => {
    const newTodo = {
      ...get().editedTodo,
      id: get().todos.length + 1,
      completed: false,
    }
    set((state) => ({ todos: [...state.todos, newTodo] }))
  },
}))

storeの利用

  • storeを丸ごと参照すると内部のstateの更新による再レンダリングが発生する
  • 不要な再レンダリングを防ぐためにはstateごとの指定が必要
  • stateの不要な再生成を防ぐためにuseCallbackでメモ化することが推奨されている

Memoizing selectors

export const TodoList = () => {
  // store丸ごと
  const store = useStore()
  // stateごと
  const todos = useStore(state => state.todos)
  const firstTodo = useStore(useCallback(state => state.todos[idx], [idx]))
  return (
    ...
  )
}

storeは複数作れる

Fetching from multiple stores

Since you can create as many stores as you like, forwarding results to succeeding selectors is as natural as it gets.
ストアはいくつでも作ることができるので、結果を後続のセレクタに転送するのはごく自然なことです。(DeepL)

const currentBear = useCredentialsStore((state) => state.currentBear)
const bear = useBearStore((state) => state.bears[currentBear])

「Reduxみたいに非同期処理の扱い方が特殊じゃないし、」
「複数のファイルをまたがって構成されてないからわかりやすいな」
「コンテキストみたいにstateを個別参照しても不要な再レンダリングが起こることがないから安心して使える」
「ただ、レンダリングを抑えるためには個別にstateを参照する必要があるのと、」
useCallbackでメモ化を考慮しなくちゃいけないのは注意が必要だ」

参考

Zustand documentation Introduction

Discussion