MobX πŸ‡ΊπŸ‡¦

MobX πŸ‡ΊπŸ‡¦

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

β€ΊTips & Tricks

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 {πŸš€}
  • Computeds with arguments {πŸš€}
  • MobX-utils {πŸš€}
  • Custom observables {πŸš€}
  • Lazy observables {πŸš€}
  • Collection utilities {πŸš€}
  • Intercept & Observe {πŸš€}

Fine-tuning

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

Subclassing

Subclassing is supported with limitations. Most notably you can only override actions/flows/computeds on prototype - you cannot override field declarations. When using makeObservable, use the override annotation for methods/getters overridden in a subclass. When using modern decorators, redecorate the overridden prototype method/getter in the subclass. Try to keep things simple and prefer composition over inheritance.

makeObservable
Modern decorators
import { makeObservable, observable, computed, action, flow, override } from "mobx"

class Parent {
// Annotated instance fields are NOT overridable
observable = 0
arrowAction = () => {}

// Non-annotated instance fields are overridable
overridableArrowAction = action(() => {})

// Annotated prototype methods/getters are overridable
action() {}
actionBound() {}
get computed() {}
*flow() {}

constructor(value) {
makeObservable(this, {
observable: observable,
arrowAction: action,
action: action,
actionBound: action.bound,
computed: computed,
flow: flow
})
}
}

class Child extends Parent {
/* --- INHERITED --- */
// THROWS - TypeError: Cannot redefine property
// observable = 5
// arrowAction = () = {}

// OK - not annotated
overridableArrowAction = action(() => {})

// OK - prototype
action() {}
actionBound() {}
get computed() {}
*flow() {}

/* --- NEW --- */
childObservable = 0
childArrowAction = () => {}
childAction() {}
childActionBound() {}
get childComputed() {}
*childFlow() {}

constructor(value) {
super()
makeObservable(this, {
// inherited
action: override,
actionBound: override,
computed: override,
flow: override,
// new
childObservable: observable,
childArrowAction: action,
childAction: action,
childActionBound: action.bound,
childComputed: computed,
childFlow: flow
})
}
}
import { observable, computed, action, flow } from "mobx"

class Parent {
// Observable state fields are inherited, but should not be re-annotated
// or overridden by subclasses.
@observable accessor observable = 0

// Decorated instance fields should not be re-annotated or overridden.
@action arrowAction = () => {}

// Non-decorated instance fields are overridable.
overridableArrowAction = action(() => {})

// Decorated prototype methods/getters are overridable.
@action action() {}
@action.bound actionBound() {}
@computed get computed() {}
@flow *flow() {}
}

class Child extends Parent {
/* --- INHERITED --- */
// Unsupported: do not re-annotate or override observable/accessor fields.
// @observable accessor observable = 5
// @action arrowAction = () => {}

// OK - not decorated
overridableArrowAction = action(() => {})

// OK - prototype
@action action() {}
@action.bound actionBound() {}
@computed get computed() {
return super.computed
}
@flow *flow() {
return yield super.flow()
}

/* --- NEW --- */
@observable accessor childObservable = 0
@action childArrowAction = () => {}
@action childAction() {}
@action.bound childActionBound() {}
@computed get childComputed() {}
@flow *childFlow() {}
}

Limitations

  1. Only action, computed, flow, action.bound defined on prototype can be overridden by subclass.
  2. Field can't be re-annotated in subclass, except with override.
  3. makeAutoObservable does not support subclassing.
  4. Extending builtins (ObservableMap, ObservableArray, etc) is not supported.
  5. You can't provide different options to makeObservable in subclass.
  6. You can't mix annotations/decorators in single inheritance chain.
  7. All other limitations apply as well

TypeError: Cannot redefine property

If you see this, you're probably trying to override arrow function in subclass x = () => {}. That's not possible because all annotated fields of classes are non-configurable (see limitations). You have two options:

1. Move function to prototype and use action.bound annotation instead

class Parent {
    // action = () => {};
    // =>
    action() {}

    constructor() {
        makeObservable(this, {
            action: action.bound
        })
    }
}
class Child {
    action() {}

    constructor() {
        super()
        makeObservable(this, {
            action: override
        })
    }
}

2. Remove action annotation and wrap the function in action manually: x = action(() => {})

class Parent {
    // action = () => {};
    // =>
    action = action(() => {})

    constructor() {
        makeObservable(this, {}) // <-- annotation removed
    }
}
class Child {
    action = action(() => {})

    constructor() {
        super()
        makeObservable(this, {}) // <-- annotation removed
    }
}

← Understanding reactivityAnalyzing reactivity {πŸš€} β†’
  • Limitations
    • TypeError: Cannot redefine property
MobX πŸ‡ΊπŸ‡¦
Docs
About MobXThe gist of MobX
Community
GitHub discussions (NEW)Stack Overflow
More
Star