5月27日 23:25
MobX 中 action 的作用和使用方法是什么?
核心答案
action 是 MobX 中修改 observable 状态的推荐方式。它将状态变更包裹在事务中,确保内部的多次修改只触发一次 reaction,同时让状态变更可追踪、可调试。
关键点:
- action 内的状态变更会批量处理,
action结束后才通知观察者 - 严格模式下(
enforceActions: 'always'),所有状态变更必须通过 action 完成 - 只对修改状态的函数使用 action,纯查询/计算用 computed
action 的三种声明方式
makeAutoObservable(推荐)
javascriptclass TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ text, completed: false }); } removeTodo(id) { this.todos = this.todos.filter(t => t.id !== id); } }
makeAutoObservable 会自动推断:有参数的方法标记为 action,getter 标记为 computed,其余为 observable。
makeObservable(需显式标注)
javascriptclass TodoStore { todos = []; constructor() { makeObservable(this, { todos: observable, addTodo: action, removeTodo: action.bound, }); } addTodo(text) { this.todos.push({ text, completed: false }); } removeTodo(id) { this.todos = this.todos.filter(t => t.id !== id); } }
action.bound 解决 this 丢失
action.bound 自动绑定 this 到实例,传给回调时不会丢失上下文:
javascriptclass Store { count = 0; constructor() { makeAutoObservable(this); } increment = action.bound(() => { this.count++; }); } const store = new Store(); document.addEventListener('click', store.increment); // this 正确
异步 action 的正确写法
async 函数中,await 之后的代码已经脱离了 action 上下文,必须用 runInAction 或 flow 包裹。
runInAction
javascriptasync fetchTodos() { this.loading = true; try { const res = await fetch('/api/todos'); const data = await res.json(); runInAction(() => { this.todos = data; this.loading = false; }); } catch (e) { runInAction(() => { this.error = e.message; this.loading = false; }); } }
flow(推荐,更简洁)
javascriptfetchTodos = flow(function* () { this.loading = true; try { const res = yield fetch('/api/todos'); const data = yield res.json(); this.todos = data; this.loading = false; } catch (e) { this.error = e.message; this.loading = false; } });
flow 用 generator 替代 async/await,每个 yield 之后自动回到 action 上下文,无需手动 runInAction。
enforceActions 配置
在 configure 中开启严格模式,强制所有状态变更走 action:
javascriptimport { configure } from 'mobx'; configure({ enforceActions: 'always' }); // 'observed' — 仅在观察者存在时强制 // 'always' — 始终强制,最严格 // 'never' — 不强制(默认)
大型项目建议设为 'always',避免随意修改状态导致难以排查的 bug。
常见坑
1. async 函数中 await 后直接改状态 — 状态变更不在 action 中,严格模式下报错。用 runInAction 或 flow。
2. action.bound 和箭头函数混用 — 箭头函数本身就是绑定过的,再套 action.bound 无意义:
javascript// 错误:箭头函数不能重新绑定 increment = action.bound(() => { this.count++; }); // 正确:用普通方法 increment() { this.count++; } // 然后在 makeObservable 中标记为 action.bound
3. 在 action 中做纯计算 — 查询、过滤等不修改状态的逻辑不应标记为 action,否则 MobX 无法追踪其依赖,应使用 computed。