Skip to main content

How does MobX track dependencies?

In short

MobX automatically tracks dependencies between data (observable) and computations (computed, autorun, observer).

When you read an observable value somewhere, MobX "remembers" it as a dependency. The next time that value changes, MobX knows who to notify - only those who actually depend on it.


The core idea

MobX builds a dependency graph (a "reactivity tree"):

javascript
observable → derivation (computed, autorun, observer)

observable - a source of data derivation - anything that reads that data reaction - an action that runs on change

Every time a derivation reads an observable, MobX registers a dependency:

"This derivation depends on this observable".


Example 1 - basic mechanics

javascript
import { observable, autorun } from 'mobx' const person = observable({ name: 'Tim', age: 25, }) // autorun - a reaction (derivation) autorun(() => { console.log(`Name: ${person.name}`) })

What happens:

  1. autorun runs → executes its callback.
  2. Inside the callback, MobX notices the read of person.name.
  3. MobX registers the dependency: autorun #1 depends on person.name.
  4. When person.name changes, MobX calls autorun again.

autorun will not be called if person.age changes, because it was not used inside the callback.


Example 2 - how MobX "spies" on reads

MobX wraps every observable getter in a proxy or getter/setter that does roughly the following:

javascript
get name() { registerDependency(this, 'name') // MobX "remembers" that this field was read return this._name }

When a reactive function runs (autorun, computed, observer), MobX temporarily switches on "dependency-recording mode". Every access to an observable at that moment gets registered.


Example 3 - with computed

javascript
import { makeAutoObservable } from 'mobx' class Store { firstName = 'Tim' lastName = 'Fattore' constructor() { makeAutoObservable(this) } get fullName() { return `${this.firstName} ${this.lastName}` } } const store = new Store() autorun(() => { console.log(store.fullName) })

What MobX does:

  1. fullName depends on firstName and lastName;
  2. autorun depends on fullName;
  3. The dependency graph:
javascript
firstName → fullName → autorun lastName → fullName → autorun
  1. Changing store.firstName triggers a recomputation of fullName, and only if the value of fullName actually changed does autorun run again.

No unnecessary reactions, everything is computed "on demand".


Example 4 - a React component (observer)

javascript
import { observer } from 'mobx-react-lite' const Counter = observer(({ store }) => ( <div> <p>{store.count}</p> <button onClick={() => store.inc()}>+</button> </div> ))

When React renders <Counter>, MobX:

  1. records which observables were read (here, store.count);
  2. subscribes the component only to those fields;
  3. triggers a re-render of only this component when they change.

So every observer becomes a reaction, and MobX itself registers dependencies as they are read.


How MobX implements this internally

  1. An observable keeps a list of its subscribers (reactions).
  2. A derivation (computed/autorun/observer) keeps track of which observables it depends on.
  3. When an observable is read, MobX calls reportObserved(); when it is written, reportChanged().
  4. When an observable changes, MobX notifies all the derivations that were registered.
  5. Derivations are recomputed only if the value they actually depend on was affected.

An important optimization

MobX doesn't just "recompute everything from scratch"; it does fine-grained dependency tracking:

  • autorun recomputes only if its specific dependencies changed.
  • computed values are cached and recomputed lazily (only when needed).
  • React observer components re-render only if the observables they used changed.

Summary

ElementRole
observabledata that MobX tracks
computeda derived value that MobX recomputes on changes
reaction / autorun / observerwhat runs when something changed
dependency graphthe internal structure that connects them
fine-grained trackingonly real dependencies trigger reactions

The MobX formula

"Anything that can be derived from the state, should be derived automatically."

Everything that can be computed from the state should be computed automatically.

And MobX does this by tracking dependencies at the level of observable read operations.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.