Zustand存储状态不持久化:登录token刷新后丢失
问题解答
这是预期行为吗?
是的。Zustand的默认状态存储在内存中,页面刷新后浏览器会重置内存状态,因此store会回到初始的token: null;而localStorage是持久化存储在浏览器磁盘上的,不会随页面刷新消失,所以仍能找到token。
优雅实现状态持久化的两种方式
方式1:手动从localStorage初始化store
创建store时直接从localStorage读取已保存的值作为初始状态,同时在状态更新时同步到localStorage(你已在signIn中做了同步,这里补全其他操作如signOut)。
修改后的store代码:
import { createStore } from "zustand/vanilla"; import { APIUser } from "@portail_mem_deport/common"; import { signup, signin } from "@/lib/api/auth"; interface UserStoreState { user: APIUser | null; token: string | null; tokenCreatedAt: number | null; setUser: (newUser: APIUser) => void; signIn: (username: string, password: string) => Promise<boolean>; signUp: (username: string, password: string) => Promise<boolean>; signOut: () => void; isSignIn: () => boolean; } // 从localStorage读取初始值 const getInitialState = () => ({ user: null, // 若需持久化user,可从localStorage读取并解析 token: localStorage.getItem("token"), tokenCreatedAt: localStorage.getItem("tokenCreatedAt") ? Number(localStorage.getItem("tokenCreatedAt")) : null, }); export default createStore<UserStoreState>((set, get) => ({ ...getInitialState(), signIn: async (username, password) => { try { const resp: any = await signin(username, password); console.log("Resp token: ", resp.token); if (resp.token) { const now = Date.now(); localStorage.setItem("token", resp.token); localStorage.setItem("tokenCreatedAt", `${now}`); set({ token: resp.token, tokenCreatedAt: now, }); } } catch (error) { console.error("Error signing in:", error); return false; } return true; }, signOut: () => { localStorage.removeItem("token"); localStorage.removeItem("tokenCreatedAt"); localStorage.removeItem("user"); // 若持久化了user,同步删除 set({ token: null, tokenCreatedAt: null, user: null, }); }, isSignIn: () => { return get().token !== null; }, setUser: (newUser) => { localStorage.setItem("user", JSON.stringify(newUser)); // 持久化user set({ user: newUser }); }, signUp: async (username, password) => { try { const resp = await signup(username, password); // 按需处理注册后的状态更新 return true; } catch (error) { console.error("Error signing up:", error); return false; } }, }));
方式2:使用Zustand官方持久化中间件(更简洁)
Zustand提供的persist中间件可自动处理状态的持久化与初始化,无需手动操作localStorage。
修改后的代码:
import { createStore } from "zustand/vanilla"; import { persist, createJSONStorage } from "zustand/middleware"; import { APIUser } from "@portail_mem_deport/common"; import { signup, signin } from "@/lib/api/auth"; interface UserStoreState { user: APIUser | null; token: string | null; tokenCreatedAt: number | null; setUser: (newUser: APIUser) => void; signIn: (username: string, password: string) => Promise<boolean>; signUp: (username: string, password: string) => Promise<boolean>; signOut: () => void; isSignIn: () => boolean; } export default createStore( persist<UserStoreState>( (set, get) => ({ user: null, token: null, tokenCreatedAt: null, signIn: async (username, password) => { try { const resp: any = await signin(username, password); console.log("Resp token: ", resp.token); if (resp.token) { const now = Date.now(); set({ token: resp.token, tokenCreatedAt: now, }); } } catch (error) { console.error("Error signing in:", error); return false; } return true; }, signOut: () => { set({ token: null, tokenCreatedAt: null, user: null, }); }, isSignIn: () => { return get().token !== null; }, setUser: (newUser) => { set({ user: newUser }); }, signUp: async (username, password) => { try { const resp = await signup(username, password); return true; } catch (error) { console.error("Error signing up:", error); return false; } }, }), { name: "user-storage", // localStorage存储的键名 storage: createJSONStorage(() => localStorage), // 指定用localStorage存储 } ) );
这种方式下,中间件会自动同步状态到localStorage,页面刷新时也会自动加载存储的状态,代码更简洁。
另外注意:你调用signIn的代码中,const { signIn, token } = userStore.getState();里的token是解构时的初始值,不会自动更新。若需要获取最新的token,应在需要时调用userStore.getState().token,或在React组件中使用useStore钩子订阅状态变化。
内容的提问来源于stack exchange,提问作者Yasha_ops
相关产品推荐
相关产品推荐

