O que é MobX?
MobX é uma biblioteca de gerenciamento de estado reativo. Você marca seus dados como observáveis e o MobX rastreia tudo o que os lê. Quando os dados mudam, os valores e componentes que dependem deles são atualizados automaticamente. Não há necessidade de escrever selectors nem de manter subscriptions.
A filosofia é quase o oposto do Redux. Enquanto o Redux torna cada mudança explícita e rastreável por meio de actions e reducers, o MobX torna as mudanças implícitas através do rastreamento automático de dependências. Você escreve código orientado a objetos comum, e a reatividade acontece nos bastidores.
Observables e makeAutoObservable
A maneira mais simples de construir uma store é através de uma classe anotada com makeAutoObservable, que infere o que deve ser observable, computed e action.
// 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();
O makeAutoObservable trata campos como observables, getters como computed values e métodos como actions. Você escreve JavaScript puro — this.todos.push(...) — e o MobX faz o rastreamento.
Valores computados
Um valor computado deriva dados de observables. Ele é cacheado e é recalculado apenas quando uma de suas dependências é alterada.
// 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;
}
}
Como total é computado, lê-lo em cem lugares diferentes custa apenas um cálculo. Ele também permanece consistente: altere o preço de um item e todos os leitores verão o novo total sem a necessidade de qualquer invalidação manual.
Actions
Actions são as funções que alteram o estado. Marcá-las explicitamente permite que o MobX realize o batching de atualizações e mantenha as mudanças rastreáveis.
// 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;
}
}
Com makeAutoObservable você obtém isso automaticamente. O benefício das actions é o batching: diversas alterações dentro de uma única action produzem uma única reação, em vez de uma para cada atribuição. O MobX também pode forçar que o estado mude apenas dentro de actions, o que ajuda a capturar mutações acidentais.
Conectando ao React com observer
O higher-order component observer inscreve um componente React nos observables que ele lê durante a renderização.
// 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>
);
});
O componente nunca precisa listar suas dependências. O MobX as registra enquanto o componente é renderizado e o renderiza novamente quando qualquer uma delas for alterada. Apenas os componentes que realmente leem um observable alterado são atualizados, o que é, por construção, um processo de granularidade fina.
Reactions e estado assíncrono
Uma reaction executa um efeito colateral quando os dados observados mudam, fora da árvore de renderização. Use-as para logs, persistência ou sincronização com sistemas externos.
// persist.js
import { autorun } from "mobx";
autorun(() => {
localStorage.setItem("theme", settings.theme);
});
Ações assíncronas seguem o mesmo padrão de qualquer método de classe. O MobX até fornece flow para fluxos assíncronos baseados em generators, mas um método async simples com runInAction para a atualização do estado final funciona bem.
// 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;
});
}
}
Comparando o MobX
MobX, Redux e Zustand gerenciam estado, mas seus modelos mentais são diferentes.
- MobX rastreia dependências automaticamente. Você escreve código que parece mutável e recebe atualizações granulares (fine-grained) sem esforço.
- Redux torna as mudanças explícitas através de actions e reducers, o que proporciona uma estrutura rigorosa e devtools poderosas.
- Zustand fica no meio termo: uma store minúscula com seletores explícitos e quase nenhuma cerimônia.
Se você gosta de stores orientadas a objetos e não gosta de escrever seletores, o MobX é um prazer. Se prefere atualizações funcionais e explícitas e uma única árvore imutável, o Redux é a melhor escolha. Se você quer algo pequeno e previsível, dê uma olhada no Zustand.
Melhores práticas
- Use
makeAutoObservablepara stores eobserverpara componentes. - Mantenha dados derivados em getters
computedem vez de duplicar cálculos. - Marque as alterações de estado como actions para que as atualizações sejam agrupadas e permaneçam rastreáveis.
- Mantenha as stores como classes simples e evite armazenar estados de UI não observáveis nelas.
- Use
runInActionpara atualizações de estado após umawait. - Mantenha dados do servidor em uma biblioteca de data-fetching em vez de uma store MobX.
- Não leia observables fora de uma reaction ou de um observer esperando que ocorram atualizações.
Erros comuns
- Esquecer o
observere questionar por que o componente não atualiza. - Ler um observable apenas uma vez e fazer o cache do valor, perdendo a reatividade.
- Armazenar objetos grandes que não são observables e não detectar as alterações.
- Mutar o estado fora de uma action quando o strict mode está ativado.
- Usar reações em excesso para coisas que deveriam ser computadas.
- Tratar o MobX como um cache de estado do servidor.
Próximos passos
O MobX é uma abordagem madura e automática para estado reativo. Compare-o com o modelo explícito do Redux Toolkit e o minimalismo do Zustand, e mova os dados do servidor para o TanStack Query. Depois, crie uma store pequena e observe a pouca configuração necessária para manter a UI sincronizada.