Skip to main content

What is action in MobX?

Simple definition

action in MobX is a function that changes observable data.

MobX tracks all changes made inside an action and updates dependencies (components, computations) as a single batch, without extra reactions between intermediate steps.


Why action exists

MobX separates two kinds of code:

KindPurpose
Reactivecomputes or renders data (for example, a React component or computed)
Activechanges data (for example, a click event, an API request, etc.)

Action is the boundary where changes to observable state happen. They provide:

  1. predictability (all changes are controlled);
  2. optimization (updates are batched);
  3. clean reactivity (no reactions "in the middle" of computations).

Example 1 - without action

javascript
import { makeObservable, observable } from 'mobx' class Counter { count = 0 constructor() { makeObservable(this, { count: observable, }) } increment() { this.count++ // MobX still works, but not optimally } }

Here increment() changes the state, but MobX does not know that this is a controlled action.


Example 2 - with action

javascript
import { makeObservable, observable, action } from 'mobx' class Counter { count = 0 constructor() { makeObservable(this, { count: observable, increment: action, // declare it as an action }) } increment() { this.count++ } }

Now MobX knows that increment() is an action, and:

  • it does not run reactions (UI updates) after every mutation;
  • it delays the reaction until the end of the action;
  • it groups all changes into a single batch → fewer re-renders and computations.

Automatic action detection

If you use modern MobX (via makeAutoObservable), it marks methods as action itself, and getters as computed:

javascript
import { makeAutoObservable } from 'mobx' class TodoStore { todos = [] constructor() { makeAutoObservable(this) // everything is automatic } addTodo(text) { this.todos.push({ text, done: false }) // action } get completed() { return this.todos.filter(t => t.done) // computed } }

Why this matters

Suppose you update several properties in a row:

javascript
userStore.updateProfile = action(() => { userStore.name = 'Alex' userStore.age = 25 userStore.email = 'alex@example.com' })

Without action, MobX would trigger a reaction after every assignment, up to three re-renders in a row. With action, MobX does this as one batch, triggering the reaction only once, after the block finishes.


Types of action

TypeDescription
action(fn)manually wraps a function in an action
@action (decorator)the old syntax for classes
makeObservable(..., { method: action })explicit declaration
makeAutoObservable()automatically detects methods as actions

Example 3 - explicit declaration outside a class

javascript
import { observable, action } from 'mobx' const state = observable({ count: 0, inc: action(function () { this.count++ }), })

Under the hood

Action:

  1. Marks the start of a "batch of changes" (startBatch);
  2. Disables intermediate reactions;
  3. Performs the changes to the observable;
  4. After finishing, notifies all dependent reactions at once (endBatch).

This is exactly what makes MobX highly efficient: the UI does not re-render on every small change, only once the whole action is done.


Summary

What it doesExample
Wraps a state changeincrement = action(() => this.count++)
Groups updatesOne batch instead of several reactions
Improves performanceFewer re-renders
Makes code predictableAll changes go through actions
Automatically created by makeAutoObservableYes

In short

action is the "official place" where you change observable data. It makes changes controlled, batched, and predictable, providing clean reactivity and high performance.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.