MobX
02 / 02

React Integration, Async Actions & When to Reach for MobX

React Integration, Async Actions & When to Reach for MobX

observer — Fine-Grained Re-Rendering

const TodoList = observer(() => (
  <ul>
    {store.todos.map((t) => (
      <li key={t.text}>{t.text}</li>
    ))}
  </ul>
))

observer subscribes a component to exactly the observables it reads during render, giving fine-grained re-rendering — only components that actually read a piece of changed state re-render, without manual selector-style subscriptions.

A Common Gotcha: Reading Outside Render

MobX tracks dependencies by recording what's read during the tracked render. Destructuring an observable value too early — before the observer-wrapped render actually accesses it — breaks fine-grained tracking for that value, a classic source of "why didn't this re-render" bugs.

Async Actions

async fetchTodos() {
  const data = await api.getTodos()
  runInAction(() => {
    this.todos = data
  })
}

Actions are meant to be synchronous, so code after an await isn't automatically covered by the outer action — runInAction wraps the resulting state mutation inline, batching it into a single reactive update.

Strict Mode & MobX State Tree

configure({ enforceActions: 'observed' }) throws if a mutation happens outside a tracked action, catching accidental stray mutations. For larger apps wanting more structure, MobX State Tree (MST) layers a typed, serializable tree model with schema validation and snapshot/time-travel support on top of MobX's reactivity.

When MobX Is a Good Fit

MobX's low-ceremony, mutation-friendly model tends to shine for rich, object-oriented domain models with lots of interdependent derived state — complex forms, drawing/editor tools — where Redux's explicit reducer boilerplate can feel heavy. Redux's explicit action→reducer flow remains easier to trace/log/replay for teams that value that discipline.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free