Using Mobx Computed Values for Efficient Derived State in React
Learn how to use Mobx computed values for efficient derived state in React. A step‑by‑step example, trade‑offs, and best practices are covered.
21 Jun 2026, 00:41 UTC

The Problem: Re‑computing Derived State
When a React component shows data that is a function of other observable state, a naïve implementation often recomputes that data on every render. If the derived calculation is expensive, or if many components share the same derivation, the cost can grow quickly. In Mobx, computed values solve this by memoizing the result and only recalculating when the underlying observables change.
Mobx Computed: Lazy, Memoized Derivations
A computed value is a read‑only property that derives its value from one or more observables. Key characteristics:
- Lazy: the computation runs the first time the value is accessed.
- Memoized: the result is cached until any dependency changes.
- Automatic dependency tracking: Mobx inspects which observables are read during the computation.
- Re‑computation is triggered only on the first access after a dependency change.
Mobx offers two convenient APIs for declaring computed properties:
makeAutoObservable– automatically turns class fields into observables, actions, or computed values based on naming conventions.makeObservable– explicit declaration, useful when you need fine‑grained control.
Practical Example: Summing an Observable Array
Below is a small store that keeps a list of numbers and exposes a computed sum. The React component displays the sum and updates only when the array changes.
// store.js
import { makeAutoObservable } from "mobx";
export class NumberStore {
numbers = [];
constructor() {
// makeAutoObservable turns:
// - class fields into observables
// - getters into computed values
// - methods that modify state into actions
makeAutoObservable(this);
}
// Computed value – automatically memoized
get sum() {
console.log("Computing sum…");
return this.numbers.reduce((a, b) => a + b, 0);
}
// Action to add a number
addNumber(n) {
this.numbers.push(n);
}
}
export const store = new NumberStore();
React component:
// SumView.jsx
import React from "react";
import { observer } from "mobx-react-lite";
import { store } from "./store";
const SumView = observer(() => {
return (
Numbers: {store.numbers.join(", ")}
Sum: {store.sum}
store.addNumber(Math.floor(Math.random() * 10))}>Add Random
);
});
export default SumView;
Key points:
- The
sumgetter is automatically treated as a computed value. - When
store.addNumbermutatesnumbers, the computed getter is invalidated. - The component is wrapped in
observer, so it re‑renders only whensumchanges. - Because
sumis lazy, the console log appears only once per change, confirming memoization.
Trade‑offs and Limitations
While computed values reduce unnecessary work, they come with considerations:
- Dependency Depth: Deep chains of computed values can lead to many layers of recomputation if an upstream dependency changes often. Benchmarking is recommended for hot paths.
- Non‑reactive Usage: Accessing a computed value outside an
observercomponent does not trigger re‑renders. If you need to expose derived data to plain functions, you must manually observe or usereaction. - Initial Access: The first access triggers the computation. If the component never reads the computed value, it remains unused, which is usually fine but worth noting for debugging.
- Action vs. Computed: Mutating state inside a computed getter (e.g., setting another observable) is a bad practice and will break Mobx’s guarantees.
When performance is critical, consider:
- Using
makeObservableto expose only the necessary computed properties. - Testing with
mobx-devtoolsto visualize dependency graphs. - Adding explicit
reactionfor side‑effects that must run whenever a computed changes.
Actionable Takeaway
For React teams looking to keep derived state efficient:
- Use
makeAutoObservablefor quick prototyping; switch tomakeObservablewhen you need explicit control. - Declare derived data as
getproperties; Mobx will treat them as computed automatically. - Wrap every component that consumes a computed value in
observer. - Verify laziness by adding a console log or using Mobx devtools.
- Watch out for deep computed chains; refactor large derivations into separate stores when necessary.
By following these steps, you can ensure that expensive calculations only run when truly needed, keeping rendering fast and state predictable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.