• This article is suitable for getting started with MOBx and provides an overview of the concepts and usage

    • Juejin. Cn/post / 684790…
  • This is the official Mobx Chinese document, which explains mobx concepts and usage in detail

    • cn.mobx.js.org/

The basic concept

  • State: data that drives the application
  • Derivations: Things that are derived from state and do not have any further interaction. Call them Views
    • Computed values: Values derived from the current state using pure functions
    • Reaction Reactions: An automatic side effect that happens when your state changes (it all ends up being I/O)
  • Actions: Code that changes state, such as events, backend pushes, functions, etc

The principle of

  • Mobx uses one-way data flow: actions change state, thus changing spawn (view)
  • All derivatives are automatically updated atomically when the state changes, so the median can never be observed
  • The calculated value is updated lazily, and any calculated value that is not in use will not be updated until it is needed for a side effect (I/O) operation. If the view is no longer used, it is automatically garbage collected
  • All computed values should be pure, they should not be used to change state (using pure functions to generate computed values)

The instance

import {observable, autorun} from 'mobx';

var todoStore = observable({
    /* Some observations */
    todos: []./* Derive the value */
    get completedCount() {
        return this.todos.filter(todo= >todo.completed).length; }});/* A function of observing state changes */
autorun(function() {
    console.log("Completed %d of %d items",
        todoStore.completedCount,
        todoStore.todos.length
    );
});

/ *.. And some state changing actions */
todoStore.todos[0] = {
    title: "Take a walk".completed: false
};
// -> Print 'Completed 0 of 1 items'

todoStore.todos[0].completed = true;
// -> Print 'Completed 1 of 1 items'
Copy the code

Action

@action.bound

Adding a decorator to a class method has the effect of binding this to the current instance, and therefore does not need to be used for class methods in the form of arrow functions (which automatically bind the current instance according to the scope chain)

runInAction

This is a simple utility function that takes blocks of code and executes them in (asynchronous) action. This is useful for creating and executing actions in real time, such as during asynchronous processes. The runInAction(f) is the syntactic sugar for action(f)().

Asynchronous action

An important point about action is: When it runs, it only triggers an update of the Observable state in the current runtime environment (or execution stack). (A special case is async/await functions. Although it looks like the block is in the same execution stack, each await function triggers a new execution stack of the asynchronous function. So only code before the first await will trigger a status update). This means that when you configure only actions to change the state, the status update will be problematic in the following cases

  • Call other non-action functions in action
  • Execute async code such as setTimeout, promise.then, async/await statements, etc. in actions if the code does not use action when changing state

Alternative methods for asynchronous action include

  1. Encapsulate asynchronous callback functions as actions
  2. The part of the asynchronous callback that changes the state is performed using the runInAction
  3. Asynchronous code is written in generator functions and wrapped in mobx.flow (function* instead of async, yield instead of await)
mobx.configure({ enforceActions: true })

class Store {
    @observable githubProjects = []
    @observable state = "pending"

    fetchProjects = flow(function * () { // <- note the * sign, this is a generator function!
        this.githubProjects = []
        this.state = "pending"
        try {
            const projects = yield fetchGithubProjectsSomehow() // use yield instead of await
            const filteredProjects = somePreprocessing(projects)
            // Blocks of asynchronous code are automatically wrapped into actions and modify state
            this.state = "done"
            this.githubProjects = filteredProjects
        } catch (error) {
            this.state = "error"}})}Copy the code

The important point

  • You can’t turn JS base type data into an Observable. You can’t do that. Mobx can only listen for reference type data
  • When @Observer is used in react component, it encapsulates its render method with Autorun, making it react to changes in Observable state and update it in time
  • There is no reliable way to inherit arrays in ES5, so observable arrays inherit from objects. This means that normal libraries have no way of recognizing observable arrays as normal arrays (such as array.isarray)isObservableArray(observable)To check if it’s an Observable array.
  • Calculated values are paused by default when not observed, unless set to force remain active
  • These reactions are garbage collected only when all the states they observe are garbage collected, so it is recommended to use cleanup functions (the ones returned by these methods) to stop them when they are no longer needed