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

MobX、TypeScript、React结合actions在严格模式下无法运行求助

解决MobX严格模式下修改Observable报错的问题

嘿,我完全懂你遇到的这个头疼问题——MobX严格模式下修改observable时的报错确实容易让人卡壳,不过既然你想坚持用严格模式(这绝对是个好习惯!),咱们来一步步解决它。

首先,这个错误的核心原因很明确:MobX严格模式禁止在action之外修改被观察的可观察值,它的目的是让所有状态变更都有迹可循,避免零散的状态修改导致的bug。关闭严格模式确实能绕过去,但长远来看严格模式能帮你写出更健壮的代码,所以咱们来把修改逻辑规范起来。

1. 直接在组件事件/定时器里修改状态?用Action包裹!

最常见的场景就是在组件的useEffect、点击事件或者定时器里直接修改observable,这时候你需要把修改逻辑包在action里,有两种常用方式:

方案A:在Store里定义专属Action方法

推荐这种方式,因为它能把状态变更的逻辑统一放在store里,更易维护:

// AppState.ts
import { makeAutoObservable } from 'mobx';

export class AppState {
  timer = 0;

  constructor() {
    // makeAutoObservable会自动把类内的方法标记为action
    makeAutoObservable(this);
  }

  // 定义修改timer的action方法
  incrementTimer = () => {
    this.timer += 1;
  };
}

然后在组件里调用这个action,而不是直接修改timer:

import { observer } from 'mobx-react-lite';
import { AppState } from './AppState';
import { useEffect } from 'react';

const TimerComponent = observer(({ appState }: { appState: AppState }) => {
  useEffect(() => {
    const interval = setInterval(() => {
      // 调用store里的action方法
      appState.incrementTimer();
    }, 1000);
    return () => clearInterval(interval);
  }, [appState]);

  return <div>Timer: {appState.timer}</div>;
});

方案B:用runInAction临时包裹修改逻辑

如果只是临时的一次性修改,不想在store里新增方法,可以用runInAction把修改逻辑包起来:

import { observer } from 'mobx-react-lite';
import { AppState } from './AppState';
import { useEffect } from 'react';
import { runInAction } from 'mobx';

const TimerComponent = observer(({ appState }: { appState: AppState }) => {
  useEffect(() => {
    const interval = setInterval(() => {
      // 把修改逻辑包裹在runInAction里
      runInAction(() => {
        appState.timer += 1;
      });
    }, 1000);
    return () => clearInterval(interval);
  }, [appState]);

  return <div>Timer: {appState.timer}</div>;
});

2. 异步操作后修改状态?别忘在Action里处理!

如果是在异步请求(比如fetch、axios)的回调里修改状态,同样要确保修改逻辑在action中,因为异步回调不属于原始的action上下文:

错误写法(会报错)

const fetchTimerData = async () => {
  const res = await fetch('/api/timer');
  const data = await res.json();
  // 异步回调里直接修改,严格模式下会报错
  appState.timer = data.timerValue;
};

正确写法1:用runInAction包裹

const fetchTimerData = async () => {
  const res = await fetch('/api/timer');
  const data = await res.json();
  // 把修改逻辑放在runInAction里
  runInAction(() => {
    appState.timer = data.timerValue;
  });
};

正确写法2:在Store里定义异步Action

把异步逻辑也放在store里,makeAutoObservable会自动把异步函数内的同步修改包裹成action:

// AppState.ts
export class AppState {
  timer = 0;

  constructor() {
    makeAutoObservable(this);
  }

  fetchTimerData = async () => {
    const res = await fetch('/api/timer');
    const data = await res.json();
    // 这里的修改会被自动识别为action的一部分
    this.timer = data.timerValue;
  };
}

3. 检查makeObservable/makeAutoObservable的配置

如果你用的是makeObservable而不是makeAutoObservable,一定要手动把修改状态的方法标记为action,否则MobX不会识别:

import { makeObservable, observable, action } from 'mobx';

export class AppState {
  timer = 0;

  constructor() {
    makeObservable(this, {
      timer: observable,
      incrementTimer: action, // 必须手动标记为action
    });
  }

  incrementTimer() {
    this.timer += 1;
  }
}

总结

核心原则就是:所有修改可观察状态的操作,必须放在MobX的action里。严格模式下这个检查是强制的,它能帮你跟踪所有状态变更的来源,让代码更可维护。按照上面的方法调整后,你应该就能在严格模式下正常运行代码了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:55:21