Global configuration options and debugging utilities for controlling MobX behavior and troubleshooting reactive dependencies. These tools help optimize performance, enforce best practices, and debug reactive flows.
Sets global configuration options that control MobX behavior across the entire application.
/**
* Configures global MobX behavior
* @param options - Configuration options
*/
function configure(options: {
/** Controls when actions are required for state modifications */
enforceActions?: "never" | "always" | "observed";
/** Whether computed values require active reactions to be created */
computedRequiresReaction?: boolean;
/** Whether reactions must observe something to be created */
reactionRequiresObservable?: boolean;
/** Whether observables require reactions to be created */
observableRequiresReaction?: boolean;
/** Isolates global state per configure call */
isolateGlobalState?: boolean;
/** Disables error boundaries for better debugging */
disableErrorBoundaries?: boolean;
/** Whether to use safe property descriptors */
safeDescriptors?: boolean;
/** Custom reaction scheduler */
reactionScheduler?: (f: () => void) => void;
/** Controls proxy usage */
useProxies?: "always" | "never" | "ifavailable";
}): void;Usage Examples:
import { configure, observable, action } from "mobx";
// Strict mode - all state changes must be in actions
configure({
enforceActions: "always",
computedRequiresReaction: true,
reactionRequiresObservable: true,
observableRequiresReaction: true
});
const state = observable({ count: 0 });
// This would throw an error in strict mode
// state.count = 1; // Error: Since strict-mode is enabled...
// Must use actions
const increment = action(() => {
state.count++;
});
increment(); // OK
// Development mode - more lenient
configure({
enforceActions: "observed", // Only when observed
disableErrorBoundaries: true // Better error messages
});Controls when actions are required for state modifications.
"never" (default): Actions not required"observed": Actions required only for observed state"always": Actions always required for state changestrue: Same as "observed"false: Same as "never"import { configure, observable, autorun } from "mobx";
const state = observable({ count: 0 });
configure({ enforceActions: "observed" });
// No observer yet - direct mutation allowed
state.count = 1; // OK
// Add observer
const dispose = autorun(() => {
console.log("Count:", state.count);
});
// Now direct mutation throws error
// state.count = 2; // Error!
// Must use action
const increment = action(() => state.count++);
increment(); // OK
dispose();When true, computed values can only be created when there's an active reaction.
import { configure, computed, observable } from "mobx";
configure({ computedRequiresReaction: true });
const state = observable({ count: 0 });
// This would throw without an active reaction
// const doubled = computed(() => state.count * 2); // Error!
// OK inside a reaction
autorun(() => {
const doubled = computed(() => state.count * 2);
console.log(doubled.get());
});Debugging utility to trace observable dependencies and understand why reactions run.
/**
* Debugging utility to trace observable dependencies
* @param thing - Optional observable to trace
* @param property - Optional property name
*/
function trace(thing?: any, property?: string): void;Usage Examples:
import { observable, computed, autorun, trace } from "mobx";
const person = observable({
firstName: "Alice",
lastName: "Smith",
age: 25
});
const fullName = computed(() => {
trace(); // Trace dependencies in this computed
return `${person.firstName} ${person.lastName}`;
});
autorun(() => {
trace(); // Trace what this reaction observes
console.log("Full name:", fullName.get());
console.log("Age:", person.age);
});
// Specific tracing
trace(person, "firstName"); // Trace specific property
trace(fullName); // Trace computed valueRegisters a global spy listener that receives all MobX events for debugging and monitoring.
/**
* Registers global spy listener for all MobX events
* @param listener - Function to receive spy events
* @returns Function to remove the spy listener
*/
function spy(listener: (event: any) => void): () => void;Usage Examples:
import { observable, action, computed, spy } from "mobx";
// Register global spy
const disposeSpy = spy((event) => {
console.log("MobX Event:", event);
});
const state = observable({ count: 0 });
const doubled = computed(() => state.count * 2);
const increment = action("increment", () => {
state.count++;
});
// These will generate spy events
increment(); // Action event
console.log(doubled.get()); // Compute event
// Remove spy
disposeSpy();Gets the dependency tree of an observable or computed value for analysis.
/**
* Gets dependency tree of observable or computed
* @param thing - Observable or computed to analyze
* @returns Dependency tree structure
*/
function getDependencyTree(thing: any): IDependencyTree;
interface IDependencyTree {
name: string;
dependencies?: IDependencyTree[];
}Gets the observer tree showing what depends on an observable.
/**
* Gets observer tree of observable
* @param thing - Observable to analyze
* @returns Observer tree structure
*/
function getObserverTree(thing: any): IObserverTree;
interface IObserverTree {
name: string;
observers?: IObserverTree[];
}Usage Examples:
import {
observable,
computed,
autorun,
getDependencyTree,
getObserverTree
} from "mobx";
const person = observable({
firstName: "Alice",
lastName: "Smith"
});
const fullName = computed(() => `${person.firstName} ${person.lastName}`);
const dispose = autorun(() => {
console.log("Name:", fullName.get());
});
// Analyze dependencies
console.log(getDependencyTree(fullName));
// Shows: fullName depends on person.firstName and person.lastName
// Analyze observers
console.log(getObserverTree(person));
// Shows: person.firstName/lastName are observed by fullName and autorun
dispose();Sets global error handler for all reactions.
/**
* Sets global error handler for reactions
* @param handler - Error handler function
* @returns Function to remove the error handler
*/
function onReactionError(handler: (error: any, derivation: any) => void): () => void;Usage Examples:
import { observable, autorun, onReactionError } from "mobx";
// Global error handler
const removeHandler = onReactionError((error, derivation) => {
console.error("Reaction error:", error.message);
console.error("In reaction:", derivation.name);
// Send to error reporting service
// errorReporting.captureException(error);
});
const state = observable({ shouldThrow: false });
autorun(() => {
if (state.shouldThrow) {
throw new Error("Something went wrong");
}
console.log("State is OK");
});
// This will trigger global error handler
state.shouldThrow = true;
// Remove handler
removeHandler();Error handling for cancelled flows.
/**
* Error thrown when flows are cancelled
*/
class FlowCancellationError extends Error {
name: "FlowCancellationError";
}
/**
* Checks if error is from flow cancellation
* @param error - Error to check
* @returns True if error is flow cancellation
*/
function isFlowCancellationError(error: any): error is FlowCancellationError;Groups multiple state changes into a single transaction (deprecated, use runInAction instead).
/**
* @deprecated Use runInAction instead
* Groups state changes into single transaction
* @param fn - Function containing state changes
* @returns Result of function
*/
function transaction<T>(fn: () => T): T;Low-level functions for accessing internal MobX state.
/**
* @internal
* Gets global MobX state
*/
function _getGlobalState(): any;
/**
* @internal
* Resets global MobX state
*/
function _resetGlobalState(): void;
/**
* @internal
* Allows state changes temporarily
* @param allowStateChanges - Whether to allow state changes
* @param fn - Function to run
*/
function _allowStateChanges<T>(allowStateChanges: boolean, fn: () => T): T;MobX automatically integrates with browser DevTools extensions when available.
// DevTools integration is automatic
declare const __MOBX_DEVTOOLS_GLOBAL_HOOK__: {
injectMobx: (mobx: any) => void;
};import { configure, trace, spy } from "mobx";
// Development configuration
if (process.env.NODE_ENV === "development") {
configure({
enforceActions: "observed",
computedRequiresReaction: false,
reactionRequiresObservable: false,
observableRequiresReaction: false,
disableErrorBoundaries: true
});
// Enable global tracing
spy((event) => {
if (event.type === "action") {
console.log(`Action: ${event.name}`, event.arguments);
}
});
}
// Production configuration
if (process.env.NODE_ENV === "production") {
configure({
enforceActions: "always",
disableErrorBoundaries: false
});
}import { spy, autorun } from "mobx";
let reactionCount = 0;
let actionCount = 0;
spy((event) => {
switch (event.type) {
case "reaction":
reactionCount++;
break;
case "action":
actionCount++;
break;
}
});
// Monitor performance
setInterval(() => {
console.log(`Reactions: ${reactionCount}, Actions: ${actionCount}`);
reactionCount = 0;
actionCount = 0;
}, 5000);interface IMobXGlobals {
version: string;
trackingDerivation: IDerivation | null;
inBatch: number;
runningReactions: boolean;
allowStateReads: boolean;
allowStateChanges: boolean;
enforceReadOnly: boolean;
spyListeners: ((event: any) => void)[];
globalReactionErrorHandlers: ((error: any, derivation: any) => void)[];
}
interface IDerivation {
name: string;
observing: IObservable[];
newObserving: null | IObservable[];
isPendingUnobservation: boolean;
runId: number;
unboundDepsCount: number;
}