MobX πŸ‡ΊπŸ‡¦

MobX πŸ‡ΊπŸ‡¦

  • API Reference
  • δΈ­ζ–‡
  • ν•œκ΅­μ–΄
  • Sponsors
  • GitHub

β€ΊFine-tuning

Introduction

  • About MobX
  • About this documentation
  • Installation
  • The gist of MobX

MobX core

  • Observable state
  • Actions
  • Computeds
  • Reactions {πŸš€}
  • API

MobX and React

  • React integration
  • React optimizations {πŸš€}

Tips & Tricks

  • Defining data stores
  • Understanding reactivity
  • Subclassing
  • Analyzing reactivity {πŸš€}
  • Error codes
  • Computeds with arguments {πŸš€}
  • MobX-utils {πŸš€}
  • Custom observables {πŸš€}
  • Lazy observables {πŸš€}
  • Collection utilities {πŸš€}
  • Intercept & Observe {πŸš€}

Fine-tuning

  • Configuration {πŸš€}
  • Decorators {πŸš€}
  • Migrating to MobX 7 {πŸš€}
  • Migrating from MobX 4/5 {πŸš€}
Edit

Migrating to MobX 7 {πŸš€}

MobX 7 is mostly a cleanup release. Most applications that already use MobX 6 idiomatically can upgrade with minimal changes.

Updating React bindings

MobX 7 keeps the React bindings split:

  • mobx-react-lite supports function components and forwardRef.
  • mobx-react is a thin wrapper around mobx-react-lite that also supports class components and the @observer class decorator.

mobx-react-lite and mobx-react require React 18 or later.

The public React binding surface has been reduced to the APIs that are still recommended:

  • observer
  • Observer
  • useLocalObservable
  • enableStaticRendering
  • isUsingStaticRendering

The following APIs have been removed:

Removed APIReplacement
disposeOnUnmountDispose reactions in componentWillUnmount, or return a cleanup function from useEffect.
PropTypesUse TypeScript or the regular prop-types package.
useLocalStoreUse useLocalObservable.
useAsObservableSourceStore the values you need locally and synchronize them from props with useEffect.
useObserverWrap the component in observer, or use the <Observer> component.
useStaticRenderingUse enableStaticRendering.
observerBatching, isObserverBatched, batchingForReactDom, batchingOptOut, batchingForReactNativeRemove these imports. React 18+ renderers handle batching automatically, and the React Native side-effect import is no longer needed.
Provider, inject, MobXProviderContext from mobx-reactUse React.createContext directly.

Migrating legacy decorators

MobX 7 supports Stage 3 decorators only.

To keep decorators, switch the class to Stage 3 decorator syntax: remove makeObservable(this) from decorated classes, drop its import when unused, and add accessor to observable fields.

-import { makeObservable, observable, computed, action } from "mobx"
+import { observable, computed, action } from "mobx"

class Todo {
-    @observable title = ""
+    @observable accessor title = ""
-    @observable finished = false
+    @observable accessor finished = false
-
-    constructor() {
-        makeObservable(this)
-    }

    @computed
    get label() {
        return `${this.finished ? "[DONE]" : "[OPEN]"} ${this.title}`
    }

    @action
    toggle() {
        this.finished = !this.finished
    }
}

Switch your compiler to modern decorators:

  • For TypeScript, use TypeScript 5 or later and disable or remove the experimentalDecorators flag.
  • For Babel, use @babel/plugin-proposal-decorators with the current Stage 3 configuration. See Enabling decorators {πŸš€} for the exact compiler setup.

If you don't want to keep decorators, remove them and pass an explicit annotation map to makeObservable:

import { makeObservable, observable, computed, action } from "mobx"

class Todo {
    title = ""
    finished = false

    constructor() {
        makeObservable(this, {
            title: observable,
            finished: observable,
            label: computed,
            toggle: action
        })
    }

    get label() {
        return `${this.finished ? "[DONE]" : "[OPEN]"} ${this.title}`
    }

    toggle() {
        this.finished = !this.finished
    }
}

Replacing namespaced APIs

Namespaced annotation and comparer properties have been replaced by named exports for better tree-shaking:

Removed APIReplacement
observable.refobservableRef
observable.shallowobservableShallow
observable.deepobservableDeep
observable.structobservableStruct
computed.structcomputedStruct
action.boundactionBound
flow.boundflowBound
comparer.identitycompareIdentity
comparer.defaultcompareDefault
comparer.structuralcompareStructural
comparer.shallowcompareShallow

Annotation map:

-import { action, comparer, computed, flow, makeObservable, observable } from "mobx"
+import { actionBound, compareStructural, computed, computedStruct, flowBound, makeObservable, observableRef } from "mobx"

 makeObservable(this, {
-    value: observable.ref,
+    value: observableRef,
-    total: computed.struct,
+    total: computedStruct,
-    rounded: computed({ equals: comparer.structural }),
+    rounded: computed({ equals: compareStructural }),
-    save: action.bound,
+    save: actionBound,
-    load: flow.bound
+    load: flowBound
 })

Decorator:

-import { action, comparer, computed, flow, observable } from "mobx"
+import { actionBound, compareStructural, computed, computedStruct, flowBound, observableRef } from "mobx"

 class Store {
-    @observable.ref accessor value = null
+    @observableRef accessor value = null

-    @computed.struct
+    @computedStruct
     get total() {
         return { value: this.value }
     }

-    @computed({ equals: comparer.structural })
+    @computed({ equals: compareStructural })
     get rounded() {
         return { value: Math.round(this.value) }
     }

-    @action.bound
+    @actionBound
     save() {}

-    @flow.bound
+    @flowBound
     *load() {}
 }

Old structural boolean options should use equals explicitly:

-computed(() => value, { compareStructural: true })
+computed(() => value, { equals: compareStructural })

-reaction(() => value, effect, { compareStructural: true })
+reaction(() => value, effect, { equals: compareStructural })

Proxy support is required

MobX 7 requires native Proxy support and no longer includes the ES5 fallback implementation.

Remove useProxies from configure calls:

 import { configure } from "mobx"

 configure({
     enforceActions: "observed",
-    useProxies: "ifavailable"
 })

Also remove { proxy: false } from observable, observable.object and observable.array options:

-const todos = observable.object({}, {}, { proxy: false })
+const todos = observable.object({})

Removed trace

The trace API has been removed. For debugging reactivity, use getDependencyTree, getObserverTree, spy, the MobX developer tools, or packages such as mobx-log.

import { autorun, getDependencyTree } from "mobx"

const disposer = autorun(() => {
    console.log(message.title)
})

console.log(getDependencyTree(disposer))

Replacing Provider and inject

Provider and inject were removed. Use React context directly. Keep the context value stable and mutate the observable store instead of replacing the provider value.

Before:

import { Provider, inject, observer } from "mobx-react"

// prettier-ignore
const UserName = inject("userStore")(
    observer(({ userStore }) => <span>{userStore.name}</span>)
)

const App = ({ userStore }) => (
    <Provider userStore={userStore}>
        <UserName />
    </Provider>
)

After, using a function component:

import React, { createContext, useContext } from "react"
import { observer } from "mobx-react"

const RootStoreContext = createContext(null)

export const RootStoreProvider = ({ rootStore, children }) => (
    <RootStoreContext.Provider value={rootStore}>{children}</RootStoreContext.Provider>
)

export const useRootStore = () => {
    const store = useContext(RootStoreContext)
    if (!store) {
        throw new Error("RootStoreProvider is missing")
    }
    return store
}

const UserName = observer(() => {
    const { userStore } = useRootStore()
    return <span>{userStore.name}</span>
})

const App = () => (
    <RootStoreProvider rootStore={{ userStore: new UserStore() }}>
        <UserName />
    </RootStoreProvider>
)

After, using a class component:

import React from "react"
import { observer } from "mobx-react"

const RootStoreContext = React.createContext(null)

class UserName extends React.Component {
    static contextType = RootStoreContext

    render() {
        const { userStore } = this.context
        return <span>{userStore.name}</span>
    }
}

const ObservedUserName = observer(UserName)
← Decorators {πŸš€}Migrating from MobX 4/5 {πŸš€} β†’
  • Updating React bindings
  • Migrating legacy decorators
  • Replacing namespaced APIs
  • Proxy support is required
  • Removed trace
  • Replacing Provider and inject
MobX πŸ‡ΊπŸ‡¦
Docs
About MobXThe gist of MobX
Community
GitHub discussions (NEW)Stack Overflow
More
Star