在 Next.js 中使用 Zustand 的持久中间件
原文来自 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)