the core concepts of MobX:
Observable state
. Any value that can be mutated and might serve as source for computed values is state. MobX can make most types of values (primitives, arrays, classes, objects, etc.) and even (potentially cyclic) references observable out of the box.Computed values
. Any value that can be computed by using a function that purely operates on other observable values. Computed values can range from the concatenation of a few strings up to deriving complex object graphs and visualizations. Because computed values are observable themselves, even the rendering of a complete user interface can be derived from the observable state. Computed values might evaluate either lazily or in reaction to state changes.Reactions
. A reaction is a bit similar to a computed value, but instead of producing a new value it produces a side effect. Reactions bridge reactive and imperative programming for things like printing to the console, making network requests, incrementally updating the React component tree to patch the DOM, etc.Actions
. Actions are the primary means to modify the state. Actions are not a reaction to state changes but take sources of change, like user events or incoming web-socket connections, to modify the observable state.
the usage of MobX:
- Use the @observable decorator or observable(object or array) functions to make objects trackable for MobX.
- The @computed decorator can be used to create functions that can automatically derive their value from the state.
- Use autorun to automatically run functions that depend on some observable state. This is useful for logging, making network requests, etc.
- Use the @observer decorator from the mobx-react package to make your React components truly reactive. They will update automatically and efficiently. Even when used in large complex applications with large amounts of data.
class Person {
@observable firstName = "Michel";
@observable lastName = "Weststrate";
@observable nickName;
@computed get fullName() {
return this.firstName + " " + this.lastName;
}
}
const michel = new Person();
// Reaction: log the profile info whenever it changes
autorun(() => console.log(person.nickName ? person.nickName : person.fullName));
// Example React component that observes state
const profileView = observer(props => {
if (props.person.nickName)
return <div>{props.person.nickName}</div>
else
return <div>{props.person.fullName}</div>
});
// Action:
setTimeout(() => michel.nickName = "mweststrate", 5000)
React.render(React.createElement(profileView, { person: michel }), document.body);
an important principle behind the design MobX is
A minimal, consistent set of subscriptions can only be achieved if subscriptions are determined at run-time.
the second important principle behind the design MobX is
for any app that is more complex than TodoMVC, you will often need a data graph, instead of a normalized tree, to store the state in a mentally manageable yet optimal way. Graphs enable referential consistency and avoid data duplication so that it can be guaranteed that derived values are never stale
How MobX keeps all derivations efficiently in a consistent state ?
The solution: don’t cache, derive instead.
MobX doesn’t run all derivations, but ensures that only computed values that are involved in some reaction are kept in sync with the observable state. Those derivations are called to be reactive.
So what about computations that aren’t used directly or indirectly by a reaction? You can still inspect the value of a computed value like fullName at any time. The solution is simple: if a computed value is not reactive, it will be evaluated on demand (lazily), just like a normal getter function. Lazy derivations (which never observe anything) can simply be garbage collected if they run out of scope. Remember that computed values should always be pure functions of the observable app state? This is the reason why: For pure functions it doesn’t matter whether they are evaluated lazily or eagerly; the evaluation of the function always yields the same result given the same observable state.
When an observable value is modified the following algorithm is performed:
- The observable value sends a stale notification to all its observers to indicate that it has become stale. Any affected computed values will recursively pass on the notification to their observers. As a result, a part of the dependency tree will be marked as stale.
- After sending the stale notification and storing the new value, a ready notification will be sent. This message also indicates whether the value did actually change.
- As soon as a derivation has received a ready notification for every stale notification received in step 1, it knows that all the observed values are stable and it will start to recompute.
- If none of the ready messages indicate that a value was changed, the derivation will simply tell its own observers that it is ready again, but without changing its value. Otherwise the computation will recompute and send a ready message to its own observers.