在 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)