乐闻世界logo
搜索文章和话题

What are the differences between makeObservable, makeAutoObservable, and decorators in MobX?

2月21日 15:50

MobX provides several tools for creating and managing observable state, including makeObservable, makeAutoObservable, and decorators. Understanding their differences and use cases is crucial for using MobX correctly.

1. makeObservable

Basic Usage

javascript
import { makeObservable, observable, computed, action } from 'mobx'; class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeObservable(this, { count: observable, firstName: observable, lastName: observable, fullName: computed, increment: action, decrement: action.bound }); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } decrement = () => { this.count--; }; }

Characteristics

  • Explicit declaration: Need to explicitly declare the type of each property
  • High flexibility: Can precisely control the behavior of each property
  • Type-safe: Good integration with TypeScript
  • Requires configuration: Needs to be called in the constructor

Use Cases

  • Need precise control over each property's behavior
  • Using TypeScript
  • Need custom configuration

Advanced Usage

javascript
class Store { data = []; loading = false; error = null; constructor() { makeObservable(this, { data: observable, loading: observable, error: observable, itemCount: computed, fetchData: action, clearData: action }, { autoBind: true }); // Auto-bind this } get itemCount() { return this.data.length; } async fetchData() { this.loading = true; try { const response = await fetch('/api/data'); this.data = await response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } } clearData() { this.data = []; this.error = null; } }

2. makeAutoObservable

Basic Usage

javascript
import { makeAutoObservable } from 'mobx'; class Store { count = 0; firstName = 'John'; lastName = 'Doe'; constructor() { makeAutoObservable(this); } get fullName() { return `${this.firstName} ${this.lastName}`; } increment() { this.count++; } decrement = () => { this.count--; }; }

Characteristics

  • Automatic inference: Automatically infers the type of properties
  • Concise: Less boilerplate code
  • Smart inference:
    • getter → computed
    • method → action
    • field → observable
  • Overridable: Can override default inference

Use Cases

  • Rapid development
  • Don't need precise control
  • Code simplicity is priority

Advanced Usage

javascript
class Store { data = []; loading = false; error = null; _internalState = {}; // Properties starting with underscore won't be auto-inferred constructor() { makeAutoObservable(this, { // Override default inference data: observable.deep, fetchData: flow, _internalState: false // Don't make it observable }); } get itemCount() { return this.data.length; } fetchData = flow(function* () { this.loading = true; try { const response = yield fetch('/api/data'); this.data = yield response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } }); }

3. Decorators

Basic Usage

javascript
import { observable, computed, action } from 'mobx'; class Store { @observable count = 0; @observable firstName = 'John'; @observable lastName = 'Doe'; @computed get fullName() { return `${this.firstName} ${this.lastName}`; } @action increment() { this.count++; } @action.bound decrement = () => { this.count--; }; }

Characteristics

  • Declarative: Uses decorator syntax
  • Concise: More readable code
  • Requires configuration: Needs Babel or TypeScript support
  • Optional in MobX 6: Decorators are no longer required

Use Cases

  • Project already has decorator support configured
  • Prefer decorator syntax
  • Need compatibility with MobX 4/5

Advanced Usage

javascript
import { observable, computed, action, flow } from 'mobx'; class Store { @observable data = []; @observable loading = false; @observable error = null; @computed get itemCount() { return this.data.length; } @action async fetchData() { this.loading = true; try { const response = await fetch('/api/data'); this.data = await response.json(); } catch (error) { this.error = error.message; } finally { this.loading = false; } } @action.bound clearData() { this.data = []; this.error = null; } }

Comparison of the Three

FeaturemakeObservablemakeAutoObservableDecorators
Declaration styleExplicit configAuto inferenceDecorators
Code volumeMoreLessLess
FlexibilityHighMediumHigh
TypeScript supportGoodGoodGood
Configuration requiredYesNoNeed Babel/TS
MobX 6 recommendedYesYesOptional

Selection Guide

Use makeObservable when:

  • Need precise control over each property's behavior
  • Using TypeScript
  • Need custom configuration
  • Need to override default behavior
javascript
class Store { data = []; constructor() { makeObservable(this, { data: observable.shallow, // Shallow observable itemCount: computed, fetchData: action }); } }

Use makeAutoObservable when:

  • Rapid development
  • Don't need precise control
  • Code simplicity is priority
  • Using MobX 6
javascript
class Store { data = []; constructor() { makeAutoObservable(this); } }

Use decorators when:

  • Project already has decorator support configured
  • Prefer decorator syntax
  • Need compatibility with MobX 4/5
javascript
class Store { @observable data = []; }

Integration with TypeScript

makeObservable + TypeScript

typescript
import { makeObservable, observable, computed, action } from 'mobx'; class Store { count: number = 0; firstName: string = 'John'; lastName: string = 'Doe'; constructor() { makeObservable<Store>(this, { count: observable, firstName: observable, lastName: observable, fullName: computed, increment: action }); } get fullName(): string { return `${this.firstName} ${this.lastName}`; } increment(): void { this.count++; } }

makeAutoObservable + TypeScript

typescript
import { makeAutoObservable } from 'mobx'; class Store { count: number = 0; firstName: string = 'John'; lastName: string = 'Doe'; constructor() { makeAutoObservable(this); } get fullName(): string { return `${this.firstName} ${this.lastName}`; } increment(): void { this.count++; } }

Decorators + TypeScript

typescript
import { observable, computed, action } from 'mobx'; class Store { @observable count: number = 0; @observable firstName: string = 'John'; @observable lastName: string = 'Doe'; @computed get fullName(): string { return `${this.firstName} ${this.lastName}`; } @action increment(): void { this.count++; } }

Best Practices

1. MobX 6 Recommends makeAutoObservable

javascript
// Recommended class Store { count = 0; constructor() { makeAutoObservable(this); } } // Can also use makeObservable class Store { count = 0; constructor() { makeObservable(this, { count: observable }); } }

2. Use makeObservable to Override Default Behavior

javascript
class Store { data = []; constructor() { makeAutoObservable(this, { data: observable.shallow // Override default deep observable }); } }

3. Use action.bound or autoBind

javascript
class Store { count = 0; constructor() { makeAutoObservable(this, {}, { autoBind: true }); } increment() { this.count++; // this auto-bound } }

4. Private Property Handling

javascript
class Store { data = []; _privateData = []; // Starts with underscore, won't be auto-inferred constructor() { makeAutoObservable(this, { _privateData: false // Explicitly don't make it observable }); } }

Common Issues

1. Decorators Not Working

Ensure:

  • Configured Babel or TypeScript decorator support
  • Using correct decorator syntax
  • MobX version supports decorators

2. makeAutoObservable Inference Error

javascript
// If inference is wrong, use makeObservable for explicit declaration class Store { data = []; constructor() { makeAutoObservable(this, { data: observable.shallow // Explicit declaration }); } }

3. TypeScript Type Errors

javascript
// Use generic parameters class Store { count = 0; constructor() { makeObservable<Store>(this, { count: observable }); } }

Summary

In MobX 6, it's recommended to use makeAutoObservable for rapid development and makeObservable for precise control. Decorators are still available but no longer required. The choice depends on project requirements and personal preference.

标签:Mobx