action.bound #
action.bound 是 MobX 提供的一个便捷方法,它可以自动将 action 方法中的 this 绑定到实例,避免手动绑定 this 的麻烦。
为什么需要action.bound? #
this绑定问题 #
在 JavaScript 中,当方法作为回调传递时,this 会丢失:
javascript
class Counter {
count = 0;
increment() {
this.count++;
}
}
const counter = new Counter();
// 直接调用 - this 正确
counter.increment(); // OK
// 作为回调传递 - this 丢失
const button = document.querySelector('button');
button.addEventListener('click', counter.increment); // Error: this is undefined
传统解决方案 #
方案1:构造函数中绑定 #
javascript
class Counter {
count = 0;
constructor() {
this.increment = this.increment.bind(this);
}
increment() {
this.count++;
}
}
方案2:箭头函数 #
javascript
class Counter {
count = 0;
increment = () => {
this.count++;
};
}
方案3:使用action.bound #
javascript
import { makeObservable, observable, action } from 'mobx';
class Counter {
count = 0;
constructor() {
makeObservable(this, {
count: observable,
increment: action.bound
});
}
increment() {
this.count++;
}
}
使用方式 #
makeObservable中使用 #
javascript
import { makeObservable, observable, action } from 'mobx';
class Store {
count = 0;
constructor() {
makeObservable(this, {
count: observable,
increment: action.bound,
decrement: action.bound,
reset: action.bound
});
}
increment() {
this.count++;
}
decrement() {
this.count--;
}
reset() {
this.count = 0;
}
}
const store = new Store();
// 可以安全地解构
const { increment, decrement, reset } = store;
increment(); // OK
decrement(); // OK
reset(); // OK
makeAutoObservable中使用 #
方式1:autoBind选项 #
javascript
import { makeAutoObservable } from 'mobx';
class Store {
count = 0;
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
increment() {
this.count++;
}
}
const store = new Store();
const { increment } = store;
increment(); // OK
方式2:overrides配置 #
javascript
import { makeAutoObservable, action } from 'mobx';
class Store {
count = 0;
constructor() {
makeAutoObservable(this, {
increment: action.bound
});
}
increment() {
this.count++;
}
}
装饰器语法 #
javascript
import { observable, action, makeObservable } from 'mobx';
class Store {
@observable count = 0;
@action.bound
increment() {
this.count++;
}
constructor() {
makeObservable(this);
}
}
完整示例 #
事件处理 #
javascript
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
increment() {
this.count++;
}
decrement() {
this.count--;
}
reset() {
this.count = 0;
}
incrementBy(amount) {
this.count += amount;
}
}
const counterStore = new CounterStore();
// React 组件
const Counter = observer(() => (
<div>
<p>Count: {counterStore.count}</p>
{/* 直接传递方法,无需箭头函数 */}
<button onClick={counterStore.increment}>+1</button>
<button onClick={counterStore.decrement}>-1</button>
<button onClick={counterStore.reset}>Reset</button>
<button onClick={() => counterStore.incrementBy(10)}>+10</button>
</div>
));
定时器 #
javascript
import { makeAutoObservable } from 'mobx';
class TimerStore {
seconds = 0;
intervalId = null;
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
start() {
if (this.intervalId) return;
this.intervalId = setInterval(() => {
this.seconds++;
}, 1000);
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
reset() {
this.stop();
this.seconds = 0;
}
toggle() {
if (this.intervalId) {
this.stop();
} else {
this.start();
}
}
}
const timer = new TimerStore();
// 可以直接作为事件处理函数
document.getElementById('startBtn').addEventListener('click', timer.start);
document.getElementById('stopBtn').addEventListener('click', timer.stop);
document.getElementById('resetBtn').addEventListener('click', timer.reset);
document.getElementById('toggleBtn').addEventListener('click', timer.toggle);
表单处理 #
javascript
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
class FormStore {
values = {
username: '',
email: '',
password: ''
};
errors = {};
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
handleChange(field) {
return (event) => {
this.values[field] = event.target.value;
this.validateField(field);
};
}
// 或者使用更通用的方式
setValue(field, value) {
this.values[field] = value;
this.validateField(field);
}
validateField(field) {
const value = this.values[field];
switch (field) {
case 'username':
this.errors.username = value.length < 3 ? 'Username too short' : '';
break;
case 'email':
this.errors.email = !/^[^\s@]+@[^\s@]+$/.test(value) ? 'Invalid email' : '';
break;
case 'password':
this.errors.password = value.length < 8 ? 'Password too short' : '';
break;
}
}
handleSubmit(event) {
event.preventDefault();
// 验证所有字段
Object.keys(this.values).forEach(field => {
this.validateField(field);
});
if (Object.values(this.errors).every(e => !e)) {
console.log('Form submitted:', this.values);
}
}
reset() {
this.values = { username: '', email: '', password: '' };
this.errors = {};
}
}
const formStore = new FormStore();
const Form = observer(() => (
<form onSubmit={formStore.handleSubmit}>
<div>
<input
name="username"
value={formStore.values.username}
onChange={formStore.handleChange('username')}
/>
{formStore.errors.username && <span>{formStore.errors.username}</span>}
</div>
<div>
<input
name="email"
value={formStore.values.email}
onChange={formStore.handleChange('email')}
/>
{formStore.errors.email && <span>{formStore.errors.email}</span>}
</div>
<div>
<input
name="password"
type="password"
value={formStore.values.password}
onChange={formStore.handleChange('password')}
/>
{formStore.errors.password && <span>{formStore.errors.password}</span>}
</div>
<button type="submit">Submit</button>
<button type="button" onClick={formStore.reset}>Reset</button>
</form>
));
action.bound vs 箭头函数 #
| 特性 | action.bound | 箭头函数 |
|---|---|---|
| 语法 | 普通方法 | 箭头函数属性 |
| 原型方法 | 是 | 否 |
| 继承 | 可被覆盖 | 不可覆盖 |
| 内存 | 共享 | 每个实例一份 |
javascript
// action.bound - 原型方法
class Store1 {
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
increment() {
this.count++;
}
}
// 箭头函数 - 实例属性
class Store2 {
increment = () => {
this.count++;
};
}
注意事项 #
1. 不能用于继承覆盖 #
javascript
class Parent {
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
method() {
console.log('parent');
}
}
class Child extends Parent {
method() {
console.log('child');
// super.method() 不能使用,因为 action.bound 绑定了 this
}
}
2. 静态方法无效 #
javascript
class Store {
static staticMethod() {
// action.bound 对静态方法无效
}
}
3. 与普通action的区别 #
javascript
class Store {
constructor() {
makeObservable(this, {
method1: action, // 不绑定 this
method2: action.bound // 绑定 this
});
}
method1() {
this.count++; // 需要正确调用
}
method2() {
this.count++; // this 始终正确
}
}
总结 #
action.bound 的优势:
- 自动绑定this:无需手动绑定
- 代码简洁:减少样板代码
- 安全传递:可以安全地作为回调传递
使用场景:
- 作为事件处理函数
- 作为定时器回调
- 作为 Promise 回调
- 需要解构使用的方法
继续学习 runInAction,了解如何在异步操作中更新状态。
最后更新:2026-03-28