mobxjs/mobx · error

[mobx-react] `observer(${displayName}).componentDidMount` mu

Error message

[mobx-react] `observer(${displayName}).componentDidMount` must be defined on prototype.
`componentDidMount = () => {}` or `componentDidMount = function() {}` is not supported.

What it means

mobx-react replaces componentDidMount on class components wrapped with observer() so it can start its reaction on mount. The replacement checks at mount time that the instance's componentDidMount is still the prototype one; if the component defined componentDidMount as an instance arrow function or instance function property, it shadows the patched prototype method and mobx-react throws. Instance property methods are incompatible because mobx-react must bind and control the lifecycle method itself.

Source

Thrown at packages/mobx-react/src/observerClass.ts:113

    }

    prototype.render = function () {
        Object.defineProperty(this, "render", {
            // There is no safe way to replace render, therefore it's forbidden.
            configurable: false,
            writable: false,
            value: isUsingStaticRendering()
                ? originalRender
                : createReactiveRender.call(this, originalRender)
        })
        return this.render()
    }

    const originalComponentDidMount = prototype.componentDidMount
    prototype.componentDidMount = function () {
        if (__DEV__ && this.componentDidMount !== Object.getPrototypeOf(this).componentDidMount) {
            const displayName = getDisplayName(componentClass)
            throw new Error(
                `[mobx-react] \`observer(${displayName}).componentDidMount\` must be defined on prototype.` +
                    `\n\`componentDidMount = () => {}\` or \`componentDidMount = function() {}\` is not supported.`
            )
        }

        // `componentDidMount` may not be called at all. React can abandon the instance after `render`.
        // That's why we use finalization registry to dispose reaction created during render.
        // Happens with `<Suspend>` see #3492
        //
        // `componentDidMount` can be called immediately after `componentWillUnmount` without calling `render` in between.
        // Happens with `<StrictMode>`see #3395.
        //
        // If `componentDidMount` is called, it's guaranteed to run synchronously with render (similary to `useLayoutEffect`).
        // Therefore we don't have to worry about external (observable) state being updated before mount (no state version checking).
        //
        // Things may change: "In the future, React will provide a feature that lets components preserve state between unmounts"

        const admin = getAdministration(this)

View on GitHub (pinned to 01211a698b)

Solutions

  1. Convert componentDidMount from a class-field arrow function to a regular prototype method: `componentDidMount() { ... }`
  2. Bind callbacks inside the constructor or at call sites instead of relying on arrow-function class fields for lifecycle methods
  3. If an instance method is truly needed, call super-equivalent logic via the prototype: define it as a prototype method
  4. Temporarily suppress by not using observer() and using <Observer> render-prop instead, but prefer fixing the method definition

Example fix

// before
class MyComponent extends React.Component {
    componentDidMount = () => {
        this.doStuff()
    }
}
// after
class MyComponent extends React.Component {
    componentDidMount() {
        this.doStuff()
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// in component constructor (dev check)
if (process.env.NODE_ENV !== 'production' && Object.prototype.hasOwnProperty.call(this, 'componentDidMount')) {
    throw new Error('componentDidMount must be a prototype method, not a class-field arrow function')
}

Type guard

function hasPrototypeComponentDidMount(ctor: Function): boolean {
    return typeof ctor.prototype?.componentDidMount === 'function' ||
        Object.getOwnPropertyNames(ctor.prototype).includes('componentDidMount')
}

Try / catch

null

Prevention

When it happens

Trigger: Defining `componentDidMount = () => {...}` or `componentDidMount = function() {...}` as a class field (instance property) inside a class that is wrapped with observer(), then mounting the component in __DEV__ mode.

Common situations: Class components written in modern class-fields style (or converted from hooks) where all methods are arrow functions for autobinding; TypeScript projects using `useDefineForClassFields` semantics; migrating a component to observer() without converting componentDidMount to a prototype method.

Related errors


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