MobX's dependency tracking system is its core mechanism, achieving efficient reactive updates through fine-grained tracking. Here's a detailed explanation of how MobX dependency tracking works:
Basic Principles of Dependency Tracking
MobX uses the Observer Pattern and Dependency Graph to implement dependency tracking. When an observable is accessed, MobX establishes dependency relationships; when an observable is modified, MobX notifies all observers that depend on it.
Core Components
1. Reaction
Reaction is the execution unit of dependency tracking, including:
- autorun: Executes immediately and automatically re-executes when dependencies change
- reaction: Provides more fine-grained control, allowing specification of tracking function and effect function
- observer (React component): Wraps React components to make them responsive to state changes
- computed: Computed values, which are also a special type of reaction
2. Derivation
Derivation represents calculations or side effects that depend on observables. Each derivation maintains a list of dependencies.
3. Atom
Atom is the smallest observable unit. Each observable object, array, Map, etc., is composed of multiple atoms.
Execution Flow of Dependency Tracking
1. Tracing Phase
When a reaction executes:
javascriptautorun(() => { console.log(store.count); // Access observable });
Execution steps:
- MobX marks the current reaction as "tracking"
- When accessing
store.count, MobX records that this reaction depends on thecountatom - Continues execution, recording all accessed observables
- After execution completes, the reaction enters a "stable" state
2. Notification Phase
When an observable is modified:
javascriptrunInAction(() => { store.count++; // Modify observable });
Execution steps:
- MobX detects that the
countatom has been modified - Finds all reactions that depend on
count - Marks these reactions as "stale"
- In the next event loop, re-executes these reactions
Structure of Dependency Graph
MobX maintains a bidirectional dependency graph:
- Atom → Derivation: Each atom knows which derivations depend on it
- Derivation → Atom: Each derivation knows which atoms it depends on
This bidirectional relationship allows MobX to efficiently perform dependency updates and cleanup.
Fine-grained Updates
MobX's dependency tracking is fine-grained, which means:
- Only updates parts that truly need updating
- Avoids unnecessary recalculations and re-renders
- Automatically handles nested dependencies
Example:
javascriptclass Store { @observable firstName = 'John'; @observable lastName = 'Doe'; @observable age = 30; @computed get fullName() { return `${this.firstName} ${this.lastName}`; } } const observerComponent = observer(() => { // Only depends on fullName, not on age return <div>{store.fullName}</div>; });
When age changes, the component won't re-render; only when firstName or lastName changes will it re-render.
Batched Updates
MobX automatically batches updates to avoid triggering reactions multiple times:
javascriptrunInAction(() => { store.firstName = 'Jane'; store.lastName = 'Smith'; store.age = 25; });
Even if multiple observables are modified, related reactions will only execute once.
Dependency Cleanup
When a reaction is no longer needed, MobX automatically cleans up dependency relationships:
- When components unmount, observers automatically clean up
- Use
dispose()method to manually clean up reactions - Avoid memory leaks
Performance Optimization
MobX's dependency tracking system provides multiple performance optimizations:
- Lazy calculation: Computed values are only calculated when needed
- Caching mechanism: Results of computed values are cached
- Batched updates: Multiple state changes are merged into one update
- Fine-grained tracking: Only tracks truly needed dependencies
Debugging Dependency Tracking
MobX provides debugging tools to view dependency relationships:
javascriptimport { trace } from 'mobx'; // Track dependencies of computed trace(store.fullName); // Track dependencies of reaction autorun(() => { console.log(store.count); }, { name: 'myReaction' });
Common Issues
1. Circular Dependencies
MobX can detect and avoid circular dependencies, but they should be avoided in design.
2. Over-tracking
Avoid accessing observables in loops or conditions, as this may lead to unnecessary dependencies.
3. Memory Leaks
Ensure reactions are cleaned up when components unmount to avoid memory leaks.
Summary
MobX's dependency tracking system achieves efficient reactive updates through the observer pattern and dependency graph. Understanding how this system works helps write more efficient MobX code and avoid common performance issues.