wheatandcatの開発ブログ

技術系記事を投稿してます

Recoil→Zustandに移行

概要

Expo SDK 53が出たのでアップデートしたら、合わせてReact 19にアップデートされた。

以前から使用していたRecoilはReact 19をサポートしていないので、Zustandに移行したので記事にした。

github.com

PR

github.com

Zustandとは

zustand-demo.pmnd.rs

  • Zustandはグローバルステートを管理するライブラリ
  • 軽量でシンプルなコードで実装できるのが特徴
  • 今回導入したアプリは、元からグローバルステートは最低限の実装しかなったので、一番シンプルなZustandを採用
  • 内部ストアの仕組みでReact Context APIを使用せず状態管理はReactからは独立しているので、RecoilのようにReact依存で使用できなくることは避けられそうなのも採用した理由の1つ

実装

導入

以下のコマンドで導入。

pnpm add zustand

ステートの更新 & 取得との移行

Recoilのコード

Recoilで情報を管理したい場合は以下のように宣言。

store/atoms.ts

import { atom } from "recoil";
import type { ItemQuery } from "@/queries/api/index";

export type Item = NonNullable<ItemQuery["item"]>;

type HomeItems = {
  items: Item[];
};

const initialHomeItemsState = (): HomeItems => ({
  items: [],
});

export const homeItemsState = atom<HomeItems>({
  key: "homeItemsState",
  default: initialHomeItemsState(),
});

値を設定したい場合は以下のように記載。

hooks/useHomeItems.tsx

import { useSetRecoilState } from "recoil";
import { homeItemsState } from "@/store/atoms";

const useHomeItems = () => {
  const setHomeItemsState = useSetRecoilState(homeItemsState);

  (略)

  useEffect(() => {
    if (prevLoading !== null && !loading) {
      const items = (data?.itemsByDate || []).map((v) => ({
        id: v?.id || "",
        title: v?.title || "",
        categoryID: v?.categoryID || 1,
        date: v?.date || "",
        like: v?.like || false,
        dislike: v?.dislike || false,
        createdAt: v?.createdAt || "",
        updatedAt: v?.updatedAt || "",
      }));
      setHomeItemsState({ items });   // ←値を設定
      setApiLoading(false);
    }
  }, [loading, prevLoading, setHomeItemsState, data]);

値を設定取得したい場合は以下のように記載。

features/home/components/Connected.tsx

import { homeItemsState } from "@/store/atoms";
import { useRecoilValue } from "recoil";

const Connected: React.FC<Props> = (props) => {
  const homeItems = useRecoilValue(homeItemsState);  // ←値を取得

Zustandのコード

Zustandでユーザーの情報を管理したい場合は以下のように宣言。

store/homeItemsStore.ts

import type { ItemQuery } from "@/queries/api/index";
import { create } from "zustand";

export type Item = NonNullable<ItemQuery["item"]>;

type HomeItems = {
  items: Item[];
};

const initialHomeItemsState = (): HomeItems => ({
  items: [],
});

export const useHomeItemsStore = create<{
  homeItems: HomeItems;
  setHomeItems: (items: Item[]) => void;
  reset: () => void;
}>((set) => ({
  homeItems: initialHomeItemsState(),
  setHomeItems: (items) => set({ homeItems: { items } }),
  reset: () => set({ homeItems: initialHomeItemsState() }),
}));

値を設定したい場合は以下のように記載。

hooks/useHomeItems.tsx

import { useHomeDateStore } from "@/store/homeDateStore";

const useHomeItems = () => {
  const setHomeItemsState = useHomeItemsStore((state) => state.setHomeItems);

  (略)

  useEffect(() => {
    if (prevLoading !== null && !loading) {
      const items = (data?.itemsByDate || []).map((v) => ({
        id: v?.id || "",
        title: v?.title || "",
        categoryID: v?.categoryID || 1,
        date: v?.date || "",
        like: v?.like || false,
        dislike: v?.dislike || false,
        createdAt: v?.createdAt || "",
        updatedAt: v?.updatedAt || "",
      }));
      setHomeItemsState(items);   // ←値を設定
      setApiLoading(false);
    }
  }, [loading, prevLoading, setHomeItemsState, data]);

値を設定取得したい場合は以下のように記載。

features/home/components/Connected.tsx

import { useHomeItemsStore } from "@/store/homeItemsStore";

const Connected: React.FC<Props> = (props) => {
  const homeItems = useHomeItemsStore((state) => state.homeItems);  // ←値を取得

上記のコード修正でステートの更新と取得が可能になる。

まとめ

上記のコード修正でサクッと移行できたので次はExpo SDK 52 → 53移行を進めていく予定。