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