概要
persist middleware を使うと、Zustand の state をローカルストレージに自動保存できる。
ページをリロードしても状態が復元されるため、テーマ設定・カート内容・ユーザー設定の保持に使う。
インストール
npm install zustand
実装例
// src/store/settingsStore.ts
import { create } from "zustand";
import { persist } from "zustand/middleware";
type Theme = "light" | "dark";
type SettingsStore = {
theme: Theme;
language: string;
setTheme: (theme: Theme) => void;
setLanguage: (language: string) => void;
};
export const useSettingsStore = create<SettingsStore>()(
persist(
(set) => ({
theme: "light",
language: "ja",
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
}),
{
name: "settings", // localStorage のキー名
// storage オプションを指定しない場合は localStorage がデフォルト
}
)
);
// src/components/ThemeToggle.tsx(hydration ミスマッチを避ける実装)
"use client";
import { useEffect, useState } from "react";
import { useSettingsStore } from "@/store/settingsStore";
export function ThemeToggle() {
const { theme, setTheme } = useSettingsStore();
const [mounted, setMounted] = useState(false);
// SSR と CSR の hydration ミスマッチを避けるため、mount 後に表示する
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return <div className="h-9 w-24 rounded border" />; // スケルトン
return (
<button
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
className="rounded border px-3 py-2 text-sm"
>
現在: {theme === "light" ? "ライト" : "ダーク"}
</button>
);
}
sessionStorage に切り替える場合
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
export const useSessionStore = create<{ count: number; increment: () => void }>()(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{
name: "session-count",
storage: createJSONStorage(() => sessionStorage), // sessionStorage に切り替え
}
)
);
ポイント
persistでラップするだけでlocalStorageへの保存・復元が自動化される- SSR 環境では
localStorageが存在しないため、初期レンダリング時に hydration ミスマッチが発生しやすい useEffect内でmountedフラグを管理し、クライアントのみで実際の値を表示するsessionStorageへの切り替えはstorage: createJSONStorage(() => sessionStorage)で行う- 保存したくないフィールドがある場合は
partializeオプションで除外できる