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.
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
- Only
action,computed,flow,action.bounddefined on prototype can be overridden by subclass. - Field can't be re-annotated in subclass, except with
override. makeAutoObservabledoes not support subclassing.- Extending builtins (
ObservableMap,ObservableArray, etc) is not supported. - You can't provide different options to
makeObservablein subclass. - You can't mix annotations/decorators in single inheritance chain.
- 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
}
}
