Redon

一心的小屋

在 Next.js 中使用 Zustand 的持久中间件

发布于 # JS # Next

原文来自 How to use Zustand’s persist middleware in Next.js,此作翻译存档。

使用持久化中间件创建存储

import { create } from "zustand";
import { persist } from "zustand/middleware";

// Custom types for theme
import { User } from "./types";

interface AuthState {
  isAuthenticated: boolean;
  user: null | User;
  token: null | string;
  login: (email: string, password: string) => Promise<void>;
  register: (userInfo: FormData) => Promise<void>;
  logout: () => void;
}

const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      isAuthenticated: false,
      user: null,
      token: null,
      login: async (email, password) => {
        // Login user code
      },
      register: async (userInfo) => {
        // Registering user code
      },
      logout: () => {
        // Logout user code
      },
    }),
    {
      name: "auth",
    }
  )
);

export default useAuthStore;

问题

如果我们尝试在组件中直接访问上述存储,当用户已登录时,就会出现水合错误,因为这与存储的初始状态不匹配。

我们会出现水合错误,是因为 Zustand 包含了来自持久化中间件(如本地存储等)的数据,而此时水合过程尚未完成,并且服务器渲染的存储具有初始状态值,这就导致了存储的状态数据出现不匹配的情况。

解决方案

我通过创建一个状态来解决这个问题,在 useEffect 钩子函数内将 Zustand 存储的状态值写入该状态中,然后使用这个状态来访问存储的值。

import { useState, useEffect } from 'react';

const useStore = <T, F>(
  store: (callback: (state: T) => unknown) => unknown,
  callback: (state: T) => F
) => {
  const result = store(callback) as F;
  const [data, setData] = useState<F>();

  useEffect(() => {
    setData(result);
  }, [result]);

  return data;
};

必须在组件中通过这个 hook 来访问存储。

const store = useStore(useAuthStore, (state) => state)