Was ist MobX?
MobX ist eine Library für reaktives State Management. Du markierst deine Daten als observable, und MobX verfolgt alles, was diese Daten liest. Wenn sich die Daten ändern, aktualisieren sich die Werte und Komponenten, die davon abhängen, automatisch. Es müssen keine Selektoren geschrieben und keine Subscriptions verwaltet werden.
Die Philosophie ist fast das Gegenteil von Redux. Während Redux jede Änderung durch Actions und Reducer explizit und nachvollziehbar macht, gestaltet MobX Änderungen durch automatische Dependency-Verfolgung implizit. Du schreibst ganz normalen objektorientierten Code, und die Reaktivität passiert im Hintergrund.
Observables und makeAutoObservable
Der einfachste Weg, einen Store zu erstellen, ist eine Klasse, die mit makeAutoObservable annotiert ist. Dadurch wird automatisch abgeleitet, was ein Observable, ein computed value oder eine Action sein soll.
// 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 behandelt Felder als Observables, Getter als computed values und Methoden als Actions. Du schreibst ganz normales JavaScript — this.todos.push(...) — und MobX trackt es.
Computed Values
Ein Computed Value leitet Daten aus Observables ab. Er wird zwischengespeichert und berechnet sich nur dann neu, wenn sich eine seiner Abhängigkeiten ändert.
// 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;
}
}
Da total berechnet wird, kostet das Auslesen an hundert verschiedenen Stellen nur eine einzige Berechnung. Zudem bleibt es konsistent: Ändern Sie den Preis eines Artikels, und jeder Leser sieht die neue Gesamtsumme, ohne dass eine manuelle Invalidierung erforderlich ist.
Actions
Actions sind die Funktionen, die den State ändern. Wenn man sie explizit als solche kennzeichnet, kann MobX Updates bündeln (Batching) und Änderungen nachvollziehbar machen.
// 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;
}
}
Mit makeAutoObservable passiert dies automatisch. Der Vorteil von Actions ist das Batching: Mehrere Änderungen innerhalb einer Action lösen eine einzige Reaction aus, anstatt einer pro Zuweisung. MobX kann zudem erzwingen, dass der State nur innerhalb von Actions geändert wird, wodurch versehentliche Mutationen abgefangen werden.
Verbindung zu React mit observer
Die Higher-Order Component observer abonniert für eine React-Komponente diejenigen Observables, die sie während des Renderings liest.
// 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>
);
});
Die Komponente muss ihre Abhängigkeiten niemals explizit auflisten. MobX zeichnet diese während des Render-Vorgangs auf und löst ein Re-Render aus, sobald sich eine dieser Abhängigkeiten ändert. Nur Komponenten, die ein geändertes Observable tatsächlich lesen, werden aktualisiert, was von Grund auf eine feingranulare Steuerung ermöglicht.
Reactions und asynchroner State
Eine Reaction führt einen Side Effect aus, wenn sich beobachtete Daten außerhalb des Render-Trees ändern. Nutze sie für Logging, Persistenz oder die Synchronisierung mit externen Systemen.
// persist.js
import { autorun } from "mobx";
autorun(() => {
localStorage.setItem("theme", settings.theme);
});
Asynchrone Actions folgen demselben Muster wie jede andere Klassenmethode. MobX liefert sogar flow für generator-basierte asynchrone Flows mit, aber eine einfache async-Methode mit runInAction für das finale State-Update funktioniert ebenfalls gut.
// 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;
});
}
}
MobX im Vergleich
MobX, Redux und Zustand verwalten alle den State, aber die mentalen Modelle unterscheiden sich.
- MobX trackt Abhängigkeiten automatisch. Du schreibst Code, der wie mutable Daten aussieht, und erhältst dafür kostenlose, feingranulare Updates.
- Redux macht Änderungen durch Actions und Reducer explizit, was eine strikte Struktur und mächtige Devtools bietet.
- Zustand liegt dazwischen: ein winziger Store mit expliziten Selectors und fast ohne Ceremony.
Wenn du objektorientierte Stores magst und es nicht gerne bist, Selectors zu schreiben, ist MobX ein Vergnügen. Wenn du explizite, funktionale Updates und einen einzigen immutable Tree bevorzugst, ist Redux die bessere Wahl. Wenn du etwas Kleines und Vorhersehbares suchst, schau dir Zustand an.
Best Practices
- Verwende
makeAutoObservablefür Stores undobserverfür Komponenten. - Nutze
computedGetter für abgeleitete Daten, anstatt Berechnungen zu duplizieren. - Markiere Zustandsänderungen als Actions, damit Updates gebündelt werden und nachvollziehbar bleiben.
- Halte Stores als einfache Klassen und vermeide es, nicht-observable UI-Zustände darin zu speichern.
- Verwende
runInActionfür Zustandsaktualisierungen nach einemawait. - Speichere Serverdaten in einer Data-Fetching-Library anstatt in einem MobX Store.
- Lies Observables nicht außerhalb einer Reaction oder eines Observers aus, wenn du Updates erwartest.
Häufige Fehler
observervergessen und sich wundern, warum die Komponente nicht aktualisiert wird.- Ein Observable nur einmal auslesen und den Wert cachen, wodurch die Reaktivität verloren geht.
- Große Nicht-Observable-Objekte speichern und dadurch Änderungen übersehen.
- State außerhalb einer Action mutieren, wenn der Strict Mode aktiviert ist.
- Übermäßiger Einsatz von Reactions für Dinge, die eigentlich über computed values gelöst werden sollten.
- MobX als Cache für Server-State verwenden.
Wie geht es weiter?
MobX ist ein ausgereifter, automatisierter Ansatz für reaktiven State. Vergleiche ihn mit dem expliziten Modell von Redux Toolkit und dem Minimalismus von Zustand und lagere Server-Daten zu TanStack Query aus. Erstelle anschließend einen kleinen Store und erlebe selbst, wie wenig Konfigurationsaufwand nötig ist, um die UI synchron zu halten.