Properties, entire objects, arrays, Maps and Sets can all be made observable.The basics of making objects observable is specifying an annotation per property using Usage: This function can be used to make existing object properties observable. Any JavaScript object (including class instances) can be passed into Alternatively, decorators like Methods that derive information and take arguments (for example Subclassing is supported with some limitations by using the class + makeObservable class + decorators factory function + makeAutoObservable observable class + decorators (legacy) class Doubler { constructor(value) { get double() { increment() { *fetch() { All annotated fields are non-configurable. When using modern decorators, there is no need to call class Doubler { constructor(value) { @computed @action @flow function createDoubler(value) { Note that classes can leverage const todosById = observable({ todosById["TODO-456"] = { const tags = observable(["high prio", "medium prio", "low prio"]) In contrast to the first example with To use legacy decorators, class Doubler { constructor(value) { @computed @action @flow Usage: The Inference rules: Usage: The The object returned by The The following example creates an observable and observes it using autorun.Working with Map and Set collections works similarly. Observable arrays have some additional nifty utility functions: Primitive values cannot be made observable by MobX since they are immutable in JavaScript (but they can be boxed).Although there is typically no use for this mechanism outside libraries. Class instances will never be made observable automatically by passing them to The primary difference between The second difference is that Because of that, The above APIs take an optional Sometimes it is necessary to convert observable data structures back to their vanilla counterparts.For example when passing observable objects to a React component that can't track observables, or to obtain a clone that should not be further mutated. To convert a collection shallowly, the usual JavaScript mechanisms work: To convert a data tree recursively to plain objects, the toJS utility can be used.For classes, it is recommended to implement a So far most examples above have been leaning towards the class syntax.MobX is in principle unopinionated about this, and there are probably just as many MobX users that use plain objects.However, a slight benefit of classes is that they have more easily discoverable APIs, e.g. TypeScript.Also, makeObservable
.The most important annotations are:makeObservable
makeObservable(target, annotations?, options?)
target
.Typically makeObservable
is used in the constructor of a class, and its first argument is this
.The annotations
argument maps annotations to each member. Only annotated members are affected.@observable
can be used on class members instead of calling makeObservable
in the constructor.findUsersOlderThan(age: number): User[]
) can not be annotated as computed
– their read operations will still be tracked when they are called from a reaction, but their output won't be memoized to avoid memory leaks. To memoize such methods you can use MobX-utils computedFn {🚀} instead.override
annotation (see the example here).import { makeObservable, observable, computed, action, flow } from "mobx"
value
makeObservable(this, {
value: observable,
double: computed,
increment: action,
fetch: flow
})
this.value = value
}
return this.value * 2
}
this.value++
}
const response = yield fetch("/api/value")
this.value = response.json()
}
}
All non-observable (stateless) fields (action
, flow
) are non-writable.makeObservable
, below is what a decorator based class looks like.Note that the @observable
annotation should always be used in combination with the accessor
keyword.import { observable, computed, action, flow } from "mobx"
@observable accessor value
this.value = value
}
get double() {
return this.value * 2
}
increment() {
this.value++
}
*fetch() {
const response = yield fetch("/api/value")
this.value = response.json()
}
}import { makeAutoObservable } from "mobx"
return makeAutoObservable({
value,
get double() {
return this.value * 2
},
increment() {
this.value++
}
})
}makeAutoObservable
as well.The difference in the examples just demonstrate how MobX can be applied to different programming styles.import { observable } from "mobx"
"TODO-123": {
title: "find a decent task management system",
done: false
}
})
title: "close all tickets older than two weeks",
done: true
}
tags.push("prio: for fun")makeObservable
, observable
supports adding (and removing) fields to an object.This makes observable
great for collections like dynamically keyed objects, arrays, Maps and Sets.makeObservable(this)
should be called in the constructor to make sure decorators work.import { observable, computed, action, flow } from "mobx"
@observable value
makeObservable(this)
this.value = value
}
get double() {
return this.value * 2
}
increment() {
this.value++
}
*fetch() {
const response = yield fetch("/api/value")
this.value = response.json()
}
}makeAutoObservable
makeAutoObservable(target, overrides?, options?)
makeAutoObservable
is like makeObservable
on steroids, as it infers all the properties by default. You can however use the overrides
parameter to override the default behavior with specific annotations —in particular false
can be used to exclude a property or method from being processed entirely.Check out the code above for an example.makeAutoObservable
function can be more compact and easier to maintain than using makeObservable
, since new members don't have to be mentioned explicitly.However, makeAutoObservable
cannot be used on classes that have super or are subclassed.observable
.getters
become computed
.setters
become action
.flow
. (Note that generator functions are not detectable in some transpiler configurations, if flow doesn't work as expected, make sure to specify flow
explicitly.)false
in the overrides
argument will not be annotated. For example, using it for read only fields such as identifiers.observable
observable(source, overrides?, options?)
@observable accessor
(field decorator)observable
annotation can also be called as a function to make an entire object observable at once.The source
object will be cloned and all members will be made observable, similar to how it would be done by makeAutoObservable
.Likewise, an overrides
map can be provided to specify the annotations of specific members.Check out the above code block for an example.observable
will be a Proxy, which means that properties that are added later to the object will be picked up and made observable as well (except when proxy usage is disabled).observable
method can also be called with collections types like arrays, Maps and Sets. Those will be cloned as well and converted into their observable counterparts.Example: observable array
import { observable, autorun } from "mobx"const todos = observable([ { title: "Spoil tea", completed: true }, { title: "Make coffee", completed: false }])autorun(() => { console.log( "Remaining:", todos .filter(todo => !todo.completed) .map(todo => todo.title) .join(", ") )})// Prints: 'Remaining: Make coffee'todos[0].completed = false// Prints: 'Remaining: Spoil tea, Make coffee'todos[2] = { title: "Take a nap", completed: false }// Prints: 'Remaining: Spoil tea, Make coffee, Take a nap'todos.shift()// Prints: 'Remaining: Make coffee, Take a nap'
clear()
removes all current entries from the array.replace(newItems)
replaces all existing entries in the array with new ones.remove(value)
removes a single item by value from the array. Returns true
if the item was found and removed.Note: primitives and class instances are never converted to observables
observable
or assigning them to an observable
property.Making class members observable is considered the responsibility of the class constructor.{🚀} Tip: observable (proxied) versus makeObservable (unproxied)
make(Auto)Observable
and observable
is that the first one modifies the object you are passing in as first argument, while observable
creates a clone that is made observable.observable
creates a Proxy
object, to be able to trap future property additions in case you use the object as a dynamic lookup map.If the object you want to make observable has a regular structure where all members are known up-front, we recommend to use makeObservable
as non proxied objects are a little faster, and they are easier to inspect in the debugger and console.log
.make(Auto)Observable
is the recommended API to use in factory functions.Note that it is possible to pass { proxy: false }
as an option to observable
to get a non proxied clone.Available annotations
Annotation Description observable
observable.deep
Defines a trackable field that stores state. If possible, any value assigned to observable
is automatically converted to (deep) observable
, autoAction or flow
based on it's type. Only plain object
, array
, Map
, Set
, function
, generator function
are convertible. Class instances and others are untouched.observable.ref
Like observable
, but only reassignments will be tracked. The assigned values are completely ignored and will NOT be automatically converted to observable
/autoAction/flow
. For example, use this if you intend to store immutable data in an observable field.observable.shallow
Like observable.ref
but for collections. Any collection assigned will be made observable, but the contents of the collection itself won't become observable.observable.struct
Like observable
, except that any assigned value that is structurally equal to the current value will be ignored.action
Mark a method as an action that will modify the state. Check out actions for more details. Non-writable. action.bound
Like action, but will also bind the action to the instance so that this
will always be set. Non-writable.computed
Can be used on a getter to declare it as a derived value that can be cached. Check out computeds for more details. computed.struct
Like computed
, except that if after recomputing the result is structurally equal to the previous result, no observers will be notified.true
Infer the best annotation. Check out makeAutoObservable for more details. false
Explicitly do not annotate this property. flow
Creates a flow
to manage asynchronous processes. Check out flow for more details. Note that the inferred return type in TypeScript might be off. Non-writable.flow.bound
Like flow, but will also bind the flow to the instance so that this
will always be set. Non-writable.override
Applicable to inherited action, flow, computed, action.bound overridden by subclass. autoAction
Should not be used explicitly, but is used under the hood by makeAutoObservable
to mark methods that can act as action or derivation, based on their calling context. It will be determined at runtime if the function is a derivation or action.Limitations
make(Auto)Observable
only supports properties that are already defined. Make sure your compiler configuration is correct, or as work-around, that a value is assigned to all properties before using make(Auto)Observable
. Without correct configuration, fields that are declared but not initialized (like in class X { y; }
) will not be picked up correctly.makeObservable
can only annotate properties declared by its own class definition. If a sub- or superclass introduces observable fields, it will have to call makeObservable
for those properties itself.options
argument can be provided only once. Passed options
are "sticky" and can NOT be changed later (eg. in subclass).override
). The field annotation or configuration can't change in subclass.
Can be disabled with configure({ safeDescriptors: false }) {🚀☣️} .action
, flow
) are non-writable.
Can be disabled with configure({ safeDescriptors: false }) {🚀☣️} .makeObservable<MyStore, "privateField" | "privateField2">(this, { privateField: observable, privateField2: observable })
make(Auto)Observable
and providing annotations must be done unconditionally, as this makes it possible to cache the inference results.make(Auto)Observable
has been called is not supported.#field
) are not supported. When using TypeScript, it is recommended to use the private
modifier instead.makeObservable
,extendObservable
cannot be used on other builtin observable types (ObservableMap
, ObservableSet
, ObservableArray
, etc)makeObservable(Object.create(prototype))
copies properties from prototype
to created object and makes them observable
. This behavior is wrong, unexpected and therefore deprecated and will likely change in future versions. Don't rely on it.Options {🚀}
options
argument which is an object that supports the following options:autoBind: true
uses action.bound
/flow.bound
by default, rather than action
/flow
. Does not affect explicitely annotated members.deep: false
uses observable.ref
by default, rather than observable
. Does not affect explicitely annotated members.name: <string>
gives the object a debug name that is printed in error messages and reflection APIs.proxy: false
forces observable(thing)
to use non-proxy implementation. This is a good option if the shape of the object will not change over time, as non-proxied objects are easier to debug and faster. This option is not available for make(Auto)Observable
, see avoiding proxies.Note: options are sticky and can be provided only once
options
argument can be provided only for target
that is NOT observable yet.
It is NOT possible to change options once the observable object was initialized.
Options are stored on target and respected by subsequent makeObservable
/extendObservable
calls.
You can't pass different options in subclass.Converting observables back to vanilla JavaScript collections
const plainObject = { ...observableObject }const plainArray = observableArray.slice()const plainMap = new Map(observableMap)
toJSON()
method, as it will be picked up by JSON.stringify
.A short note on classes
instanceof
checks are really powerful for type inference, and class instances aren't wrapped in Proxy
objects, giving them a better experience in debuggers.Finally, classes benefit from a lot of engine optimizations, since their shape is predictable, and methods are shared on the prototype.But heavy inheritance patterns can easily become foot-guns, so if you use classes, keep them simple.So, even though there is a slight preference to use classes, we definitely want to encourage you to deviate from this style if that suits you better.
Creating observable state · MobX 🇺🇦 (2024)
Table of Contents
makeObservable
makeAutoObservable
observable
Available annotations
Limitations
Options {🚀}
Converting observables back to vanilla JavaScript collections
A short note on classes
Top Articles
THE CHALLENGES OF NIGHT TIME BEACH PHOTOGRAPHY (and how to overcome them)
Summer Bucket List - Ideas and Planner - Add A Little Adventure
MacAddict Jan07: Free Mac Software, Safari Plug-ins, Linux on Mac, Mac Reviews, Mac Software Reviews, Ipod Reviews, Mac Games - [PDF Document]
Poisoned cough syrup killed kids. Authorities cut the investigation short
Latest Posts
Das Cinch! Pop Up Zelt – käferfrei und einfach riesig — CamperStyle
11 Best Pop up Camping Tents for Quick & Easy Setup [2024]
Article information
Author: Rueben Jacobs
Last Updated:
Views: 6073
Rating: 4.7 / 5 (57 voted)
Reviews: 88% of readers found this page helpful
Author information
Name: Rueben Jacobs
Birthday: 1999-03-14
Address: 951 Caterina Walk, Schambergerside, CA 67667-0896
Phone: +6881806848632
Job: Internal Education Planner
Hobby: Candle making, Cabaret, Poi, Gambling, Rock climbing, Wood carving, Computer programming
Introduction: My name is Rueben Jacobs, I am a cooperative, beautiful, kind, comfortable, glamorous, open, magnificent person who loves writing and wants to share my knowledge and understanding with you.