Derived values that automatically recalculate when their observable dependencies change, with built-in caching and lazy evaluation. Computed values are pure functions of observable state and only recalculate when their dependencies change.
Creates computed values that automatically track their observable dependencies and cache results until dependencies change.
/**
* Creates a computed value that recalculates when dependencies change
* @param fn - Function that returns the computed value
* @param options - Configuration options for the computed value
* @returns Computed value with get() method
*/
function computed<T>(fn: () => T, options?: IComputedValueOptions<T>): IComputedValue<T>;Usage Examples:
import { observable, computed, autorun } from "mobx";
const person = observable({
firstName: "Alice",
lastName: "Smith"
});
// Create computed value
const fullName = computed(() => `${person.firstName} ${person.lastName}`);
// Access computed value
console.log(fullName.get()); // "Alice Smith"
// React to changes
autorun(() => {
console.log("Full name:", fullName.get());
});
// Modify dependencies - computed recalculates automatically
person.firstName = "Bob"; // Logs: "Full name: Bob Smith"Decorator for creating computed properties in classes.
/**
* Decorator for computed properties in classes
* Can be used as @computed or @computed(options)
*/
interface IComputedFactory extends Annotation, PropertyDecorator {
<T>(options: IComputedValueOptions<T>): Annotation & PropertyDecorator;
struct: Annotation & PropertyDecorator;
}
declare const computed: IComputedFactory;Usage Examples:
import { makeObservable, observable, computed } from "mobx";
class Person {
firstName = "Alice";
lastName = "Smith";
constructor() {
makeObservable(this, {
firstName: observable,
lastName: observable,
fullName: computed,
initials: computed
});
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get initials() {
return `${this.firstName[0]}.${this.lastName[0]}.`;
}
}
// With automatic detection
class PersonAuto {
firstName = "Alice";
lastName = "Smith";
constructor() {
makeAutoObservable(this); // Automatically detects getters as computed
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}Computed values that use structural comparison instead of reference equality for change detection.
/**
* Computed decorator with structural equality comparison
* Only triggers reactions when the computed result structurally changes
*/
declare const struct: Annotation & PropertyDecorator;Usage Examples:
import { makeObservable, observable, computed } from "mobx";
class TodoList {
todos = [];
constructor() {
makeObservable(this, {
todos: observable,
todoStats: computed.struct // Uses structural equality
});
}
get todoStats() {
return {
total: this.todos.length,
completed: this.todos.filter(t => t.completed).length,
active: this.todos.filter(t => !t.completed).length
};
}
}
// The computed will only trigger reactions when the actual
// structure of the returned object changes, not on every recalculationInterface for computed values providing access to the computed result.
interface IComputedValue<T> {
/** Get the current computed value */
get(): T;
/** Set a new value (only if setter is defined) */
set?(value: T): void;
/** Observe changes to the computed value */
observe(listener: (change: IComputedDidChange<T>) => void): () => void;
/** Check if the computed is currently being computed */
isComputing(): boolean;
}
interface IComputedDidChange<T> {
type: "compute";
object: IComputedValue<T>;
newValue: T;
oldValue: T;
}Computed values can optionally have setters for bidirectional computed properties.
/**
* Creates a computed value with both getter and setter
* @param options - Configuration with get and set functions
* @returns Computed value with get() and set() methods
*/
function computed<T>(options: {
get(): T;
set?(value: T): void;
name?: string;
equals?: IEqualsComparer<T>;
}): IComputedValue<T>;Usage Examples:
import { observable, computed } from "mobx";
const person = observable({
firstName: "Alice",
lastName: "Smith"
});
// Computed with setter
const fullName = computed({
get() {
return `${person.firstName} ${person.lastName}`;
},
set(value) {
const parts = value.split(" ");
person.firstName = parts[0];
person.lastName = parts[parts.length - 1];
}
});
console.log(fullName.get()); // "Alice Smith"
fullName.set("Bob Johnson");
console.log(person.firstName); // "Bob"
console.log(person.lastName); // "Johnson"Creates computed values that accept arguments, useful for computed values that depend on parameters.
/**
* Creates a computed function that can accept arguments
* Note: Arguments are not reactive, only the function body is
*/
function computed<Args extends any[], Return>(
fn: (...args: Args) => Return,
options?: IComputedValueOptions<Return>
): (...args: Args) => Return;Usage Examples:
import { observable, computed } from "mobx";
const inventory = observable.map([
["apple", 10],
["banana", 5],
["orange", 8]
]);
// Computed function with arguments
const getItemStatus = computed((itemName: string, threshold: number) => {
const count = inventory.get(itemName) || 0;
return {
name: itemName,
count,
status: count < threshold ? "low" : "ok"
};
});
console.log(getItemStatus("apple", 5)); // { name: "apple", count: 10, status: "ok" }
console.log(getItemStatus("banana", 10)); // { name: "banana", count: 5, status: "low" }interface IComputedValueOptions<T> {
/** Debug name for the computed value */
name?: string;
/** Custom equality comparer function */
equals?: IEqualsComparer<T>;
/** Whether the computed requires an active observer to be kept alive */
requiresReaction?: boolean;
/** Whether to keep the computed value alive even without observers */
keepAlive?: boolean;
}
type IEqualsComparer<T> = (a: T, b: T) => boolean;Computed values can handle errors gracefully and provide fallback values.
import { observable, computed, autorun } from "mobx";
const data = observable({
value: "10",
multiplier: 2
});
const safeCalculation = computed(() => {
try {
const num = parseFloat(data.value);
if (isNaN(num)) {
throw new Error("Invalid number");
}
return num * data.multiplier;
} catch (error) {
console.warn("Calculation error:", error.message);
return 0; // Fallback value
}
});
autorun(() => {
console.log("Result:", safeCalculation.get());
});Understanding and debugging computed dependencies.
import { observable, computed, trace, getDependencyTree } from "mobx";
const person = observable({
firstName: "Alice",
lastName: "Smith",
age: 25
});
const fullName = computed(() => {
// Use trace() to debug dependencies
trace();
return `${person.firstName} ${person.lastName}`;
});
// Get dependency information
console.log(getDependencyTree(fullName));
// The computed only depends on firstName and lastName,
// not age - so changing age won't trigger recalculationimport { observable, computed, autorun } from "mobx";
const expensiveList = observable([]);
// Use structural equality for computed values that return objects/arrays
const processedList = computed(() => {
console.log("Processing list..."); // Only logs when list actually changes
return expensiveList
.filter(item => item.active)
.map(item => ({ ...item, processed: true }))
.sort((a, b) => a.name.localeCompare(b.name));
}, {
equals: (a, b) => {
// Custom equality check
return JSON.stringify(a) === JSON.stringify(b);
}
});
// This will only trigger when the processed result actually changes
autorun(() => {
console.log("Processed count:", processedList.get().length);
});