MobX、TypeScript、React结合actions在严格模式下无法运行求助
嘿,我完全懂你遇到的这个头疼问题——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

