In MobX, observable, computed, and action are three core concepts, each with different purposes and characteristics:
Observable
Purpose:
- Create trackable state
- Notify all observers that depend on it when state changes
Usage:
javascriptimport { observable } from 'mobx'; class Store { @observable count = 0; @observable user = { name: 'John', age: 30 }; @observable items = []; } // Or using functional API const store = observable({ count: 0, user: { name: 'John', age: 30 }, items: [] });
Characteristics:
- Can decorate class properties or create observable objects
- Supports objects, arrays, Maps, Sets, and other data structures
- Deeply observable: nested objects also become observable automatically
- In MobX 6, uses decorator API by default or
makeObservable
Computed
Purpose:
- Create derived values that are automatically calculated based on other observable states
- Has caching mechanism, only recalculates when dependencies change
- Avoids repeated calculations, improves performance
Usage:
javascriptimport { observable, computed } from 'mobx'; class Store { @observable firstName = 'John'; @observable lastName = 'Doe'; @computed get fullName() { return `${this.firstName} ${this.lastName}`; } } // Or using functional API const store = observable({ firstName: 'John', lastName: 'Doe' }); const fullName = computed(() => `${store.firstName} ${store.lastName}`);
Characteristics:
- Read-only property (by default)
- Automatically caches calculation results
- Only recalculates when dependent observables change
- Can be used by other computed values or observers
- Lazy calculation: only calculates when accessed
Action
Purpose:
- Encapsulate state modification logic
- Ensure state modifications are trackable and predictable
- In MobX 6, all state modifications must be done within actions
Usage:
javascriptimport { observable, action } from 'mobx'; class Store { @observable count = 0; @action increment() { this.count++; } @action.bound decrement = () => { this.count--; }; @action async fetchData() { this.loading = true; const data = await api.getData(); this.data = data; this.loading = false; } } // Or using functional API const store = observable({ count: 0 }); store.increment = action(() => { store.count++; });
Characteristics:
- Mandatory in MobX 6
- Can be synchronous or asynchronous
action.boundautomatically bindsthis- Can be nested
- Provides better debugging experience and predictability
Relationship Between the Three
- Observable is the foundation, providing observable state
- Computed depends on observables, automatically calculating derived values
- Action is used to modify observables, triggering dependency updates
Best Practices
-
Using observable:
- Only use observable for state that needs to be tracked
- Avoid using observable for entire application state
- Use
makeAutoObservablefor automatic inference
-
Using computed:
- Use for calculating derived values, not in components
- Avoid side effects in computed values
- Use computed for complex calculation logic
-
Using action:
- All state modifications should be done within actions
- Async operations should also be wrapped in actions
- Use
action.boundto avoid binding issues
Performance Considerations
- Observable itself doesn't bring performance overhead
- Computed improves performance through caching mechanism
- Action helps batch updates, reducing unnecessary re-renders
Understanding the differences between these three concepts and using them correctly is key to mastering MobX.