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

Next.js中MobX异步拉取数据后状态更新未触发页面渲染

问题描述

Next.js + TypeScript 项目中集成MobX做状态管理,Store中定义的API调用方法可正常发起请求、获取返回数据,控制台日志可确认Store内部状态已完成更新,但已渲染的页面不会同步刷新,无法展示接口返回的实际数据。

复现代码

Store 实现代码

interface SerializedStore  {
    PageTitle: string;
    content: string;
    isOpen: boolean;
    companiesDto: CompanyDto[],
    companyCats: string[]
};

export class AwardStore {
    PageTitle: string = 'Client Experience Awards';
    companiesDto : CompanyDto[] = [];
    companyCats: string[] = [];
    loadingInitial: boolean = true
    constructor() {
        makeAutoObservable(this)
    }


    hydrate(serializedStore: SerializedStore) {
        this.PageTitle = serializedStore.PageTitle != null ? serializedStore.PageTitle : "Client Experience Awards";
        this.companyCats = serializedStore.companyCats != null ? serializedStore.companyCats : [];
        this.companiesDto = serializedStore.companiesDto != null ? serializedStore.companiesDto : [];
    }

    changeTitle = (newTitle: string) => {
        this.PageTitle = newTitle;
    }

    loadCompanies = async () => {
        this.setLoadingInitial(true);
        axios.get<CompanyDto[]>('MyAPICall')
            .then((response) => {
                runInAction(() => {
                    this.companiesDto = response.data.sort((a, b) => a.name.localeCompare(b.name));
                    response.data.map((company : CompanyDto) => {
                        if (company.categories !== null ) {
                            company.categories?.forEach(cat => {    
                                this.addNewCateogry(cat)
                            })
                        }
                    })
                    console.log(this.companyCats);
                    this.setLoadingInitial(false);
                })
            })
            .catch(errors => {
                this.setLoadingInitial(false);
                console.log('There was an error getting the data: ' + errors);
            })
    }

    addNewCateogry = (cat : string) => {
        this.companyCats.push(cat);
    }

    setLoadingInitial = (state: boolean) => {
        this.loadingInitial = state;
    }
}

export async function fetchInitialStoreState() {
    // 初始Store状态获取逻辑
    return {};
}

应用入口 _app.js 实现代码

class MyApp extends App {
  constructor(props) {
      super(props);
      this.state = {
          awardStore: new AwardStore()
      };
      this.state.awardStore.loadCompanies();
    }

  // 获取序列化的Store初始状态
  static async getInitialProps(appContext) {
      const appProps = await App.getInitialProps(appContext);
      const initialStoreState = await fetchInitialStoreState();

      return {
          ...appProps,
          initialStoreState
      };
  }

  // 将序列化状态注水到Store实例
  static getDerivedStateFromProps(props, state) {
      state.awardStore.hydrate(props.initialStoreState);
      return state;
  }

  render() {
      const { Component, pageProps } = this.props;
      return (
          <Provider awardStore={this.state.awardStore}>
              <Component {...pageProps} />
          </Provider>
      );
  }
}
export default MyApp;
故障原因
  • 静态渲染配置错误:原代码在模块顶层执行enableStaticRendering(isServer),Next.js构建阶段会将服务端侧的判断结果固定为true,导致客户端环境下MobX的响应式追踪机制被禁用,状态变更无法通知到订阅的组件。
  • 注水逻辑执行时机错误:getDerivedStateFromProps会在组件每次渲染前执行,每次都会用空的初始Store状态覆盖客户端已经更新完成的Store数据,接口返回的数据刚写入Store就被重置为空值。
  • 组件未订阅状态变更:消费Store状态的页面/组件没有包裹MobX的observer高阶组件,不会监听Store的状态变化事件,状态更新后组件不会触发重渲染。
  • 拼写隐患:分类新增方法名拼写错误(addNewCateogry应为addNewCategory),后续调用容易出现找不到方法的问题。
修复步骤
  1. 修正静态渲染配置
    移除模块顶层的enableStaticRendering调用,仅在服务端渲染环境下开启该配置,保证客户端响应式逻辑正常运行:
if (typeof window === "undefined") {
  enableStaticRendering(true);
}
  1. 调整注水逻辑执行时机
    删除getDerivedStateFromProps中的重复注水逻辑,仅在Store实例首次初始化时执行一次注水,避免后续客户端状态被初始值覆盖:
constructor(props) {
  super(props);
  this.state = {
    awardStore: new AwardStore()
  };
  // 仅首次构造时执行一次注水
  if (props.initialStoreState) {
    this.state.awardStore.hydrate(props.initialStoreState);
  }
  this.state.awardStore.loadCompanies();
}

// 删除原有的getDerivedStateFromProps静态方法
  1. 给消费Store状态的组件添加响应式包裹
    所有读取、使用Store状态的函数组件用observer(来自mobx-react-lite)包裹,类组件用mobx-react导出的observer包裹,让组件可以订阅Store的状态变更,状态更新时自动重渲染:
import { observer } from "mobx-react-lite";

// 包裹后组件内读取的awardStore状态变更时会自动刷新
const CompanyCategoryList = observer(({ awardStore }) => {
  return (
    <div className="category-list">
      {awardStore.companyCats.map(cat => (
        <span className="category-tag" key={cat}>{cat}</span>
      ))}
    </div>
  )
})
  1. 修正方法名拼写错误
    将addNewCateogry统一修正为addNewCategory,避免后续调用出现低级错误。

内容的提问来源于stack exchange,提问作者Ivana Murray

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:27:16