¿Qué es MobX?
MobX es una librería de gestión de estado reactiva. Marcas tus datos como observables y MobX rastrea todo aquello que los lee. Cuando los datos cambian, los valores y componentes que dependen de ellos se actualizan automáticamente. No hay que escribir selectores ni mantener suscripciones.
Su filosofía es casi opuesta a la de Redux. Mientras que Redux hace que cada cambio sea explícito y rastreable a través de acciones y reducers, MobX hace que los cambios sean implícitos mediante el rastreo automático de dependencias. Escribes código orientado a objetos normal y la reactividad ocurre internamente.
Observables y makeAutoObservable
La forma más sencilla de construir un store es mediante una clase anotada con makeAutoObservable, la cual infiere qué debe ser observable, computado o una acción.
// store.js
import { makeAutoObservable } from "mobx";
class TodoStore {
todos = [];
constructor() {
makeAutoObservable(this);
}
add(text) {
this.todos.push({ id: Date.now(), text, done: false });
}
toggle(id) {
const todo = this.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
}
get remaining() {
return this.todos.filter((t) => !t.done).length;
}
}
export const todoStore = new TodoStore();
makeAutoObservable trata los campos como observables, los getters como valores computados y los métodos como acciones. Escribes JavaScript puro — this.todos.push(...) — y MobX se encarga del seguimiento.
Valores computados
Un valor computado deriva datos a partir de observables. Se almacena en caché y solo se recalcula cuando cambia una de sus dependencias.
// cart.js
class Cart {
items = [];
constructor() {
makeAutoObservable(this);
}
get total() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
get isEmpty() {
return this.items.length === 0;
}
}
Debido a que total es computado, leerlo en cien lugares diferentes solo requiere un cálculo. Además, se mantiene consistente: si cambias el precio de un artículo, todos los lectores verán el nuevo total sin necesidad de realizar ninguna invalidación manual.
Acciones
Las acciones son las funciones que cambian el estado. Marcarlas explícitamente permite que MobX agrupe las actualizaciones y mantiene los cambios rastreables.
// actions.js
import { action, makeObservable, observable } from "mobx";
class Settings {
theme = "dark";
fontSize = 16;
constructor() {
makeObservable(this, {
theme: observable,
fontSize: observable,
update: action,
});
}
update({ theme, fontSize }) {
if (theme) this.theme = theme;
if (fontSize) this.fontSize = fontSize;
}
}
Con makeAutoObservable obtienes esto automáticamente. El beneficio de las acciones es el agrupamiento (batching): varios cambios dentro de una sola acción producen una única reacción, en lugar de una por cada asignación. MobX también puede obligar a que el estado solo cambie dentro de acciones, lo que permite detectar mutaciones accidentales.
Conectando con React mediante observer
El componente de orden superior observer suscribe un componente de React a los observables que lee durante el renderizado.
// TodoList.jsx
import { observer } from "mobx-react-lite";
import { todoStore } from "./store";
export const TodoList = observer(() => {
return (
<div>
<p>{todoStore.remaining} remaining</p>
<ul>
{todoStore.todos.map((todo) => (
<li key={todo.id} onClick={() => todoStore.toggle(todo.id)}>
{todo.done ? "✓" : "○"} {todo.text}
</li>
))}
</ul>
<button onClick={() => todoStore.add("New task")}>Add</button>
</div>
);
});
El componente nunca tiene que listar sus dependencias. MobX las registra mientras el componente se renderiza y lo vuelve a renderizar cuando cualquiera de ellas cambia. Solo se actualizan los componentes que realmente leen un observable modificado, lo que garantiza un control de grano fino por diseño.
Reacciones y estado asíncrono
Una reacción ejecuta un efecto secundario cuando los datos observados cambian, fuera del árbol de renderizado. Utilízalas para el registro de logs, persistencia o sincronización con sistemas externos.
// persist.js
import { autorun } from "mobx";
autorun(() => {
localStorage.setItem("theme", settings.theme);
});
Las acciones asíncronas siguen el mismo patrón que cualquier método de clase. MobX incluso incluye flow para flujos asíncronos basados en generadores, pero un método async sencillo con runInAction para la actualización final del estado funciona perfectamente.
// users.js
import { makeAutoObservable, runInAction } from "mobx";
class UserStore {
users = [];
loading = false;
constructor() {
makeAutoObservable(this);
}
async fetchUsers() {
this.loading = true;
const res = await fetch("/api/users");
const users = await res.json();
runInAction(() => {
this.users = users;
this.loading = false;
});
}
}
Comparativa de MobX
MobX, Redux y Zustand gestionan el estado, pero sus modelos mentales son diferentes.
- MobX rastrea las dependencias automáticamente. Escribes código que parece mutable y obtienes actualizaciones granulares de forma gratuita.
- Redux hace que los cambios sean explícitos a través de acciones y reducers, lo que proporciona una estructura estricta y devtools potentes.
- Zustand se sitúa en un punto intermedio: un store diminuto con selectores explícitos y casi nada de ceremonia.
Si te gustan los stores orientados a objetos y no te gusta escribir selectores, MobX es un placer. Si prefieres actualizaciones funcionales y explícitas y un único árbol inmutable, Redux es una mejor opción. Si buscas algo pequeño y predecible, echa un vistazo a Zustand.
Mejores prácticas
- Usa
makeAutoObservablepara los stores yobserverpara los componentes. - Mantén los datos derivados en getters de
computeden lugar de duplicar los cálculos. - Marca los cambios de estado como acciones para que las actualizaciones se procesen en lotes y sean rastreables.
- Mantén los stores como clases simples y evita almacenar en ellos estado de UI que no sea observable.
- Usa
runInActionpara actualizar el estado después de unawait. - Mantén los datos del servidor en una librería de data-fetching en lugar de un store de MobX.
- No leas observables fuera de una reaction o un observer si esperas que se actualicen.
Errores comunes
- Olvidar
observery preguntarse por qué el componente no se actualiza. - Leer un observable una sola vez y cachear el valor, perdiendo la reactividad.
- Almacenar objetos grandes que no son observables y no detectar los cambios.
- Mutar el estado fuera de una action cuando el strict mode está activado.
- Abusar de las reactions para cosas que deberían ser computed.
- Tratar MobX como un cache de estado del servidor.
Próximos pasos
MobX es un enfoque maduro y automático para el estado reactivo. Compáralo con el modelo explícito de Redux Toolkit y el minimalismo de Zustand, y traslada los datos del servidor a TanStack Query. Después, construye un store pequeño y observa lo poco que hace falta configurar para mantener la UI sincronizada.