Welcome to the interactive tutorial on how to use side-effect "operators" in MobX! Over the course of the next three samples, you'll learn (and be able to explore) exactly how autorun
, when
and reaction
work, and when/why you would use them when building reactive applications.
-
-
Save Alex-of-House-Derp/76043f138989db9a9fed004de1f06854 to your computer and use it in GitHub Desktop.
<button id="setLoadingTrue">Set isLoading to true</button> | |
<button id="setLoadingFalse">Set isLoading to false</button> | |
<button id="addDynamicProp">Add dynamic prop</button> |
{ | |
"title": "Learning MobX (Side-Effects)", | |
"description": "Learning MobX (Side-Effects)", | |
"steps": [ | |
{ | |
"file": "script.js", | |
"line": 11, | |
"description": "This is cool!" | |
} | |
] | |
} |
autorun
takes a function, and immediately runs it. Upon execution, it detects any observables that the function accesses (e.g. observable.property
), and will automatically re-run the function anytime those observables change.
For example, you should have seen the Loading changed
alert display immediately when viewing/running this playground. This is because of the initial autorun
call here. If you click either of the two buttons below, you'll see the alert again.
However, if you click the same button multiple times, you won't see the alert, because the observable isn't changing. Try it for yourself!
import { observable, autorun } from "mobx"; | |
const store = observable({ | |
isLoading: false | |
}); | |
autorun(() => { | |
alert(`Loading changed: ${store.isLoading}`); | |
}); | |
autorun(() => { | |
if (store.newProp) { | |
alert(`Dynamic property added: ${store.newProp}`); | |
} | |
}) | |
document.querySelector("#setLoadingTrue").onclick = () => { | |
store.isLoading = true; | |
}; | |
document.querySelector("#setLoadingFalse").onclick = () => { | |
store.isLoading = true; | |
}; | |
document.querySelector("#addDynamicProp").onclick = () => { | |
store.newProp = "Cool!"; | |
}; |
<button>Update the store</button> |
The when
operator allows you to specify a predicate and a callback function. The predicate function will observed it until it returns true
. Once true
, your callback function is run, and then the observer is disposed.
For example, click the following button to mutate a store property, which should trigger the when
handler here. However, if you click it again, it won't trigger the handler again, since the observer has been disposed.
Click the "run playground" button in order to reset the tutorial step and try again.
import { observable, when } from "mobx"; | |
const store = observable({ | |
isLoading: false | |
}); | |
when(() => store.isLoading, () => { | |
alert("Loaded! :D"); | |
}); | |
document.querySelector("button").onclick = () => { | |
store.isLoading = true; | |
}; |
<button id="setLoadingTrue">Set isLoading to true</button> | |
<button id="setLoadingFalse">Set isLoading to false</button> |
The reaction
operator is similar to autorun
, but it allows you to control exactly which observables should be tracked.
It takes two functions: one that determines the observables that should be tracked, and a callback that will be triggered whenever any of the tracked observables as changed.
Unlike autorun
the callback is only run after the tracked observables change, as opposed to also being run immediately.
For example, if you click the two buttons below, you should see the Loading changed
alert. This is because of the reaction
call here. However, if you click the same button multiple times, you won't see the alert, because the observable isn't changing. Try it for yourself!
import { observable, reaction } from "mobx"; | |
const store = observable({ | |
isLoading: false | |
}); | |
reaction(() => [store.isLoading], () => { | |
alert(`Loading changed: ${store.isLoading}`); | |
}); | |
document.querySelector("#setLoadingTrue").onclick = () => { | |
store.isLoading = true; | |
}; | |
document.querySelector("#setLoadingFalse").onclick = () => { | |
store.isLoading = false; | |
}; |
{ | |
"tutorial": "Learning MobX (Side-Effects)", | |
"layout": "splitLeftTabbed", | |
"readmeBehavior": "previewHeader", | |
"themePreview": false | |
} |