You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:25:31