🐻

ZustandのPersisting store dataを使ってみる

に公開

この記事は「React Advent Calendar 2025」の4日目の記事です。

Reactを触って最初に驚くのは、状態管理に対する選択肢の多さです。StateReducer,Context...そして外部ライブラリのReduxZustandJotai...どんなときに、どれを使うのがよいのか?調べれば調べるほど迷路に迷い込んだ気分になります。

なるべく外部ライブラリを使わないほうが良いのかな?と思いつつ、Zustandを触ってみたところ、やりたいことが簡単にできそうだったので、今の案件で使ってみようと思ってます。

主に画面間でのパラメータ保持に使いたいと思っていて、ZustandのMiddlewareであるPersisting store dataを触りました。sessionStorageやlocalStorageが使いやすい印象です。

キーバリュー形式のパラメータを想定して、Mapを保持するためのuseStorageMapを定義します。

UseStorageMap.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import type { PersistStorage , StorageValue } from 'zustand/middleware';

interface StorageMapParam {
    param: Map<string, any>;
}M

const storage: PersistStorage<StorageMapParam>  = {
    getItem: (name) => {
      const val = localStorage.getItem(name);
      if (val === null) return null;
      const existingValue = JSON.parse(val);
      return {
        ...existingValue,
        state: {
          ...existingValue.state,
          param: new Map(existingValue.state.param),
        }
      }
    },
    setItem: (name, value: any) => {
      const str = JSON.stringify({
        ...value,
        state: {
          ...value.state,
          param: Array.from(value.state.param.entries()),
        },
      })
      
      localStorage.setItem(name, str);
    },
    removeItem: (name) => {
      localStorage.removeItem(name)
    }
}

interface StorageMapState {
    param: Map<string, any>;
    getItem: (key: string) => any;
    setItem: (val: Map<string, any>) => void;
}

export const useStorageMap = create<StorageMapState>()(
    persist(
        (set, get) => ({
            param: new Map(),
            getItem: (key: string): any => {
                return get().param.get(key);
            },
            setItem: (val: Map<string, any>) => set({ param: val }),
        }),
        {
            name: 'storage-map-param', // ストレージのキー名
            storage, // sessionStorageを使用
        },
    ),
)

呼び出し元画面でボタンを押したタイミングで、次の画面に渡したいパラメータをセットします。

from.tsx
import { useNavigate } from "react-router";
import { useStorage } from "../store/useStorage";
import { useStorageMap } from "../store/useStorageMap";

export default function from() {
    const navigate = useNavigate();
    const storeMap = useStorageMap();

    const nextPage = () => {
        storeMap.setItem(new Map([["key1", "value1"], ["key2", 42]]));
        navigate("/to");
    }

    return (
        <div>
            <button onClick={nextPage}>次の画面にすすむ</button>
        </div>
    )
}

次の画面でuseStoreMap()を使ってパラメータを取得して表示します。

to.tsx
import { useEffect } from "react";
import { useStorage } from "../store/useStorage";
import { useStorageMap } from "../store/useStorageMap";

export default function to() {
    const storeMap = useStorageMap();
    useEffect(() => {
        console.log("Stored Map Value:", storeMap.getItem("key1"));
        console.log("Stored Map Value:", storeMap.getItem("key2"));
    }, []);


    return (
        <div>
            <p>前の画面でセットされたMapの値は key1: {storeMap.getItem("key1")}, key2: {storeMap.getItem("key2")}</p>
        </div>
    )
}

Discussion