5月27日 23:25

MobX 中 action 的作用和使用方法是什么?

核心答案

action 是 MobX 中修改 observable 状态的推荐方式。它将状态变更包裹在事务中,确保内部的多次修改只触发一次 reaction,同时让状态变更可追踪、可调试。

关键点:

  • action 内的状态变更会批量处理,action 结束后才通知观察者
  • 严格模式下(enforceActions: 'always'),所有状态变更必须通过 action 完成
  • 只对修改状态的函数使用 action,纯查询/计算用 computed

action 的三种声明方式

makeAutoObservable(推荐)

javascript
class 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(需显式标注)

javascript
class 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 到实例,传给回调时不会丢失上下文:

javascript
class 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 上下文,必须用 runInActionflow 包裹。

runInAction

javascript
async 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(推荐,更简洁)

javascript
fetchTodos = 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:

javascript
import { configure } from 'mobx'; configure({ enforceActions: 'always' }); // 'observed' — 仅在观察者存在时强制 // 'always' — 始终强制,最严格 // 'never' — 不强制(默认)

大型项目建议设为 'always',避免随意修改状态导致难以排查的 bug。

常见坑

1. async 函数中 await 后直接改状态 — 状态变更不在 action 中,严格模式下报错。用 runInActionflow

2. action.bound 和箭头函数混用 — 箭头函数本身就是绑定过的,再套 action.bound 无意义:

javascript
// 错误:箭头函数不能重新绑定 increment = action.bound(() => { this.count++; }); // 正确:用普通方法 increment() { this.count++; } // 然后在 makeObservable 中标记为 action.bound

3. 在 action 中做纯计算 — 查询、过滤等不修改状态的逻辑不应标记为 action,否则 MobX 无法追踪其依赖,应使用 computed。

标签:Mobx