Why MobX Computed Values Are the Lazy, Automatic Dependency‑Tracking Heroes of React State
Learn how MobX’s computed values keep React components fast and predictable. Discover lazy evaluation, automatic dependency tracking, a real‑world example, and the trade‑offs you should know.
25 Sept 2026, 04:22 UTC

Problem: React state that keeps recomputing everything
In a typical React app, a component that reads a large piece of state will re‑render on every change, even if the part it actually uses never changed. This leads to unnecessary work and, with complex calculations, performance regressions. Developers often write plain JavaScript functions that recalc on every render, ignoring the fact that many values are derived from other pieces of state.
Thesis: MobX computed values are the lightweight, lazy, automatically‑tracked way to keep derived data efficient.
Computed values in MobX are read‑only getters that depend on observable state. They are evaluated only when accessed, and only after any of their dependencies have changed. This guarantees that expensive calculations run just once per relevant change, and that React components that observe them stay fast.
Section 1 – Lazy Evaluation: Do Not Run Until You Need It
Computed values are not executed on store creation. They sit in a dormant state until a component or another computed accesses them. This is called lazy evaluation. A typical pattern:
import { makeAutoObservable } from "mobx";
class CounterStore {
count = 0; // observable
constructor() {
makeAutoObservable(this);
}
get double() { // computed
console.log("double evaluated");
return this.count * 2;
}
}
export const counter = new CounterStore();
Run counter.double in a console. The console log appears only once. Increment counter.count and access counter.double again – the log appears again, showing that the computed re‑runs only when needed.
Section 2 – Automatic Dependency Tracking: No Manual Wiring Needed
MobX automatically records which observables a computed touches. The dependency graph is built on the first evaluation and updates reactively when any of those observables change. No explicit observe calls or subscription lists are required.
In the example above, double depends on count. If another observable, offset, is added and used inside double, MobX will notice the new dependency without any extra code:
get double() {
return (this.count + this.offset) * 2;
}
When offset changes, double will automatically be marked stale.
Section 3 – React Integration: Observer Components React Only to Needed Data
React components wrapped with observer will re‑render only when the observables they read change. Because computed values are treated as observables, a component that reads counter.double will re‑render only when count (or any new dependency) changes.
import { observer } from "mobx-react-lite";
import { counter } from "./CounterStore";
const DoubleDisplay = observer(() => {
return <div>Double: {counter.double}</div>;
});
Notice the component accesses counter.double directly. MobX tracks that dependency and triggers a re‑render only when count changes.
Section 4 – Trade‑off: Computed Values Are Read‑Only and Nested Observables Can Cost
- Read‑only nature: Attempting to assign to a computed throws an error. Provide a setter or a separate action if you need to mutate.
- Deep nesting: When a computed traverses a deeply nested observable tree, MobX must track each level, which can add overhead. Flattening state or using
makeAutoObservablewithshallow: truecan mitigate this.
Example of a setter:
set double(value) {
this.count = value / 2;
}
Now counter.double = 20 will update count accordingly.
Actionable Closing: How to Use Computed Values Effectively
- Define derived data as
getproperties inside a store created withmakeAutoObservableor@computeddecorator. - Keep computed bodies simple; avoid side effects. If you need side effects, use
autorunorreactioninstead. - Wrap React components that read computed values with
observerto get automatic re‑rendering. - Use MobX devtools or
mobx.inspectto view the dependency graph and confirm that computed values are attached to the correct observables. - For large state trees, consider flattening or using shallow observables to reduce tracking overhead.
By following these patterns, you’ll keep your React components fast, your state logic declarative, and your derived data automatically up‑to‑date without writing boilerplate subscription code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.