What does reaction() do?
What is reaction()
reaction()is a MobX function that reacts (reactively) to a change in specific observable data and runs a side effect when it changes.
You could call it an analog of React's useEffect(),
but it works at the MobX level, without any React component involved.
1. Basic syntax
import { observable, reaction } from "mobx"
const price = observable.box(100)
const quantity = observable.box(2)
// reaction: watch for a change in total
reaction(
() => price.get() * quantity.get(), // tracker function (what to watch)
(total, prevTotal) => { // effect (what to do on change)
console.log(`Total changed: ${prevTotal} → ${total}`)
}
)
price.set(120) // Total changed: 200 → 240
quantity.set(3) // Total changed: 240 → 360MobX:
- Calls the "tracker" (the first function) to figure out the dependencies,
- Watches them,
- Every time the result changes, it calls the side effect (the second function).
2. How it differs from autorun()
autorun() runs every time any dependent observable used inside its body changes.
reaction() is more targeted:
it tracks only what the first function (the selector) returns.
| autorun() | reaction() | |
|---|---|---|
| What it tracks | Everything used inside the body | Only the tracker function's result |
| When it's called | Every time a dependency changes | Only if the result actually changed |
| Used for | Logging, general reactions | Precise control over side effects |
In other words, reaction() is "fine-tuned" reactive side effects.
3. Example filtering out changes
import { makeAutoObservable, reaction } from "mobx"
class CartStore {
items = []
constructor() {
makeAutoObservable(this)
// watch the number of items
reaction(
() => this.items.length,
(count) => {
console.log(`Item count changed: ${count}`)
}
)
}
add(item) {
this.items.push(item)
}
}
const cart = new CartStore()
cart.add("T-shirt") // → Item count changed: 1
cart.add("Jeans") // → Item count changed: 2Here the reaction fires only when the array's length changes,
not on every change to items.
4. reaction() parameters
reaction(
expression, // a function that returns "what to track"
effect, // a function called on change
options? // options
)Options:
| Option | What it does | Example |
|---|---|---|
fireImmediately | run the effect immediately on initialization | { fireImmediately: true } |
equals | a custom comparison function (default !==) | { equals: (a, b) => JSON.stringify(a) === JSON.stringify(b) } |
delay | delay before calling the effect (debounce) | { delay: 300 } |
name | a name for debugging | { name: "cartReaction" } |
Example with fireImmediately
reaction(
() => store.user,
(user) => {
console.log("Current user:", user)
},
{ fireImmediately: true }
)Logs the user right on initialization,
then on every change to store.user.
5. A "side effect" example (an API request)
reaction(
() => store.searchQuery,
async (query) => {
if (query.length < 3) return
const results = await api.search(query)
store.results = results
},
{ delay: 400 } // debounce
)This is a classic scenario: reacting to an observable change (e.g. a search string), and running an asynchronous effect (an API request).
6. Difference from computed
| computed | reaction | |
|---|---|---|
| What it does | Computes a value | Triggers a side effect |
| Returns something | Yes | No |
| Caches the result | Yes | No |
| Changes state | No | Can |
| Used for | Derived data | Side effects |
computed is a "pure" value,
reaction is a "dirty action" (an effect).
7. Example in a class
class AuthStore {
user = null
constructor() {
makeAutoObservable(this)
reaction(
() => this.user?.isLoggedIn,
(isLoggedIn) => {
if (isLoggedIn) {
console.log("User logged in")
} else {
console.log("User logged out")
}
},
{ fireImmediately: true }
)
}
}This lets you react to a state change (e.g. on login / logout) without logic inside UI components.
8. Cleanup (dispose)
reaction() returns a function that stops the observation:
const dispose = reaction(
() => store.count,
(count) => console.log("count:", count)
)
dispose() // the reaction will no longer fireThis is useful for manual lifecycle management (e.g. outside React).
9. Visually
[observable] → [reaction → side effect]Example:
store.query → reaction → api.search(query)
Summary
reaction()is a reactive observer that watches specific data (an observable) and runs a side effect when it changes.
A way to remember it:
reaction(trackFn, effectFn)= "Watch the result oftrackFnand calleffectFnif it changed."
When to use it:
When you need to trigger an effect on an observable change, but not call it on every re-render. For example:
- API requests when state changes;
- Saving data to
localStorage; - Logging, tracking, analytics;
- Reacting to changes in authorization, filters, routes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.