在 MobX 中,action 是修改 observable 状态的唯一推荐方式。使用 action 可以确保状态变化是可追踪、可预测的,并且能够优化性能。
Action 的作用
- 批量更新:action 内部的所有状态变化会被批量处理,减少不必要的重新计算
- 可追踪性:所有状态变化都集中在 action 中,便于调试和追踪
- 事务性:action 内部的状态变化是原子的,要么全部成功,要么全部失败
- 性能优化:减少 reaction 的触发次数,提高性能
Action 的使用方式
1. 使用装饰器
javascriptimport { observable, action } from 'mobx'; class TodoStore { @observable todos = []; @action addTodo(text) { this.todos.push({ text, completed: false }); } @action.bound removeTodo(id) { this.todos = this.todos.filter(todo => todo.id !== id); } @action async fetchTodos() { const response = await fetch('/api/todos'); const data = await response.json(); this.todos = data; } }
2. 使用 makeObservable
javascriptimport { makeObservable, observable, action } from 'mobx'; class TodoStore { todos = []; constructor() { makeObservable(this, { todos: observable, addTodo: action, removeTodo: action.bound, fetchTodos: action }); } addTodo(text) { this.todos.push({ text, completed: false }); } removeTodo(id) { this.todos = this.todos.filter(todo => todo.id !== id); } async fetchTodos() { const response = await fetch('/api/todos'); const data = await response.json(); this.todos = data; } }
3. 使用 runInAction
javascriptimport { observable, runInAction } from 'mobx'; class TodoStore { todos = []; async fetchTodos() { const response = await fetch('/api/todos'); const data = await response.json(); runInAction(() => { this.todos = data; }); } }
Action 的类型
1. action
最基础的 action 装饰器,用于包装状态修改方法。
javascript@action updateTodo(id, updates) { const todo = this.todos.find(t => t.id === id); if (todo) { Object.assign(todo, updates); } }
2. action.bound
自动绑定 this 的 action,避免在回调函数中丢失 this 上下文。
javascript@action.bound handleClick() { this.count++; } // 使用时不需要 bind <button onClick={this.handleClick}>Click</button>
3. runInAction
在异步操作中修改状态时使用。
javascriptasync loadData() { const response = await fetch('/api/data'); const data = await response.json(); runInAction(() => { this.data = data; this.loading = false; }); }
Action 的最佳实践
1. 始终在 action 中修改状态
javascript// ❌ 错误:直接修改状态 class Store { @observable count = 0; increment() { this.count++; // 不是 action } } // ✅ 正确:在 action 中修改状态 class Store { @observable count = 0; @action increment() { this.count++; } }
2. 使用 action.bound 处理事件处理器
javascriptclass Component { @observable count = 0; @action.bound handleClick() { this.count++; } render() { return <button onClick={this.handleClick}>Click</button>; } }
3. 异步操作中使用 runInAction
javascript@action async fetchUser(id) { this.loading = true; try { const response = await fetch(`/api/users/${id}`); const data = await response.json(); runInAction(() => { this.user = data; this.loading = false; }); } catch (error) { runInAction(() => { this.error = error.message; this.loading = false; }); } }
4. 合理拆分 action
javascript// ❌ 过于复杂的 action @action handleComplexOperation(data) { this.loading = true; this.data = data; this.filtered = data.filter(item => item.active); this.count = this.filtered.length; this.timestamp = Date.now(); this.loading = false; } // ✅ 拆分为多个小 action @action handleComplexOperation(data) { this.setLoading(true); this.setData(data); this.processData(data); this.setLoading(false); } @action setLoading(loading) { this.loading = loading; } @action setData(data) { this.data = data; } @action processData(data) { this.filtered = data.filter(item => item.active); this.count = this.filtered.length; this.timestamp = Date.now(); }
常见错误
1. 在异步回调中直接修改状态
javascript// ❌ 错误 @action async fetchData() { const data = await fetch('/api/data'); this.data = await data.json(); // 不在 action 中 } // ✅ 正确 @action async fetchData() { const data = await fetch('/api/data'); runInAction(() => { this.data = data.json(); }); }
2. 忘记使用 action.bound
javascript// ❌ 错误:this 可能丢失 class Component { @action handleClick() { this.count++; } render() { return <button onClick={this.handleClick}>Click</button>; } } // ✅ 正确:使用 action.bound class Component { @action.bound handleClick() { this.count++; } render() { return <button onClick={this.handleClick}>Click</button>; } }
性能优化
- 使用 action 批量更新:减少 reaction 触发次数
- 避免在循环中创建 action:复用 action 函数
- 合理使用 runInAction:只在必要时使用
- 使用 action.bound:避免重复 bind
总结
- action 是修改 observable 状态的唯一推荐方式
- 使用 action 可以批量更新、提高性能、便于调试
- 优先使用 action.bound 处理事件处理器
- 异步操作中使用 runInAction 修改状态
- 合理拆分复杂的 action,提高可维护性