mobxjs/mobx · warning

[mobx] Computed value '${this.name_}' is being read outside

Error message

[mobx] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.

What it means

MobX computed values are cached and only recomputed when dependencies change inside a reactive context (reaction, render, autorun). If `computedRequiresReaction` (or the computed's `requiresReaction_` option) is enabled and a computed is read outside such a context, MobX warns that it must do an expensive full recompute instead of using the cache.

Source

Thrown at packages/mobx/src/core/computedvalue.ts:331

    }

    suspend_() {
        if (!this.keepAlive_) {
            clearObserving(this)
            this.value_ = undefined // don't hold on to computed value!
        }
    }

    warnAboutUntrackedRead_() {
        if (!__DEV__) {
            return
        }
        if (
            typeof this.requiresReaction_ === "boolean"
                ? this.requiresReaction_
                : globalState.computedRequiresReaction
        ) {
            console.warn(
                `[mobx] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.`
            )
        }
    }

    toString() {
        return `${this.name_}[${this.derivation.toString()}]`
    }

    valueOf(): T {
        return toPrimitive(this.get())
    }

    [Symbol.toPrimitive]() {
        return this.valueOf()
    }
}

View on GitHub (pinned to 01211a698b)

Solutions

  1. Move the read inside a reaction: wrap it in `autorun`, `reaction`, `when`, or a React component's render with `observer`.
  2. Use `untracked(() => store.total)` if the read is intentional and unreactive.
  3. If the warning is noise for your app, disable it via `configure({ computedRequiresReaction: false })`.

Example fix

// before
function handleClick() { console.log(store.total) }
// after
function handleClick() {
  untracked(() => console.log(store.total))
}
Defensive patterns

Strategy: try-catch

Validate before calling

import { configure } from 'mobx'
configure({ computedRequiresReaction: true })
// in dev/tests this surfaces any computed read happening outside a reaction
// so the offending access can be fixed before it ships

Type guard

function isReadInsideReaction() {
  try {
    const gs = require('mobx')._getGlobalState()
    return gs.trackingDerivation != null
  } catch {
    return true // internal API unavailable; assume safe
  }
}

Try / catch

import { untracked } from 'mobx'
function safeReadComputed(store) {
  try {
    return store.total
  } catch (e) {
    return untracked(() => store.total)
  }
}

Prevention

When it happens

Trigger: Reading a computed property (e.g. `store.total`) outside any reaction/observer while `configure({ computedRequiresReaction: true })` is set, or that specific computed was created with `requiresReaction: true`.

Common situations: Reading observables in event handlers, setTimeout callbacks, tests, or during logging/debugging; enabling strict reaction checks in development to catch accidental untracked reads.

Related errors


AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28). Data as JSON: /api/errors/53ad5e6082462472. Report an issue: GitHub.