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

如何在Next.js中持久化Mobx状态数据?

在Next.js中持久化Mobx状态的解决方案

问题分析

你当前的代码存在几个关键问题导致状态丢失:

  • 客户端初始化Store时,没有主动从本地存储加载持久化的数据,仅依赖传入的initialState
  • makePersistable的配置和hydrate方法没有正确配合,无法完成状态恢复
  • Next.js的客户端/服务端环境差异处理不够严谨

方案一:修复mobx-persist-store的使用

1. 调整RootStore代码

import { UserStore } from "./user";
import { hydrateStore, makePersistable } from "mobx-persist-store";
import localforage from "localforage";

export class RootStore {
  user: UserStore;

  constructor() {
    this.user = new UserStore(this);

    // 仅在客户端环境初始化持久化
    if (typeof window !== "undefined") {
      makePersistable(this.user, {
        name: "UserStore", // 改为UserStore避免命名混淆
        properties: ["details", "cart"],
        storage: localforage,
      }, { delay: 200, fireImmediately: false });
    }
  }

  // 修正hydrate逻辑,只处理userStore的持久化数据
  async hydrate() {
    if (typeof window !== "undefined") {
      await hydrateStore(this.user);
    }
  }
}

2. 修改Store Provider代码

在客户端初始化时主动触发hydrate,确保从本地存储恢复状态:

import { createContext, FC, useContext, useEffect, useState } from "react";
import { RootStore } from "./root";

const RootStoreContext = createContext<RootStore | undefined>(undefined);

export const useStore = () => {
  const context = useContext(RootStoreContext);
  if (!context) {
    throw new Error("useStore must be used within StoreProvider");
  }
  return context;
};

let rootStore: RootStore | undefined;

const initializeStore = () => {
  const _rootStore = rootStore ?? new RootStore();
  // 服务端直接返回,不处理持久化
  if (typeof window === "undefined") return _rootStore;
  
  if (!rootStore) rootStore = _rootStore;
  return _rootStore;
};

interface Props {
  children: React.ReactNode;
}

export const RootStoreProvider: FC<Props> = ({ children }) => {
  const [store] = useState(() => initializeStore());

  // 客户端组件挂载后执行hydrate恢复状态
  useEffect(() => {
    const hydrateStore = async () => {
      await store.hydrate();
    };
    hydrateStore();
  }, [store]);

  return (
    <RootStoreContext.Provider value={store}>
      {children}
    </RootStoreContext.Provider>
  );
};

方案二:手动实现持久化(无需依赖第三方库)

如果不想用mobx-persist-store,可以利用Mobx的reaction监听状态变化,手动读写localStorage:

1. 修改UserStore示例

import { makeAutoObservable, reaction } from "mobx";
import { RootStore } from "./root";

export class UserStore {
  rootStore: RootStore;
  details: any = null;
  cart: any[] = [];

  constructor(rootStore: RootStore) {
    this.rootStore = rootStore;
    makeAutoObservable(this);

    // 初始化时从localStorage加载数据
    this.loadFromStorage();

    // 监听状态变化,自动保存到localStorage
    reaction(
      () => ({ details: this.details, cart: this.cart }),
      (state) => {
        localStorage.setItem("userStore", JSON.stringify(state));
      }
    );
  }

  // 从localStorage加载数据
  loadFromStorage() {
    if (typeof window === "undefined") return;
    const saved = localStorage.getItem("userStore");
    if (saved) {
      const parsed = JSON.parse(saved);
      this.details = parsed.details;
      this.cart = parsed.cart;
    }
  }

  // 登出时清除持久化数据
  logout() {
    this.details = null;
    this.cart = [];
    localStorage.removeItem("userStore");
  }
}

2. 调整RootStore和Provider

RootStore无需再处理持久化逻辑,Provider保持单例初始化即可:

// RootStore.ts
import { UserStore } from "./user";

export class RootStore {
  user: UserStore;

  constructor() {
    this.user = new UserStore(this);
  }
}

// root-store-provider.ts 保持你原来的单例逻辑,无需hydrate相关代码

关键注意事项

  • 服务端渲染兼容:所有持久化操作必须包裹在typeof window !== "undefined"判断中,避免服务端执行时出错
  • 登出逻辑:无论用哪种方案,登出时都要主动清除本地存储中的用户数据,避免残留
  • 数据序列化:如果存储的是复杂对象,确保能被JSON序列化(或使用localforage支持的其他类型)

内容的提问来源于stack exchange,提问作者Abdullah Al Mamun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:35:30