Developer Docs

Introduction to MobX

For state management, C#Bot uses MobX. MobX is an observable framework for Javascript which allows for an implementation of the ‘observer’ design pattern. MobX simplifies state management and provides the mechanism to store and update the application state, which is then used by React.

There are five main features of MobX that are key to understanding how it works. These are observerable, action, reaction, computed, and autorun. We will now discuss these features in greater detail.

observable, action, and reaction

Observables, actions, and reactions are best demonstrated together since their functionality relies on each other. Take this code example:

import { action, observable, reaction } from 'mobx';

// A class to store some data inside
class Data {
    @observable
    value = 0;

    @action
    incrementValue = () => this.value += 1;

    constructor() {
        // Create a reaction that listens to the value property changing
        // and log a message to the console when that happens
        reaction(() => this.value, () => console.log('Value Changed'));
    }
}

// Create a new instance of the class
const myData = new Data();

// After this function is called, 'Value Changed' will be printed to the console
myData.incrementValue();

An observable is a variable where we can store data that will be observed.

An action is a function which can change the observable data.

A reaction is something that occurs once an observable has changed, so we can perform side effects. A reaction is a function which takes two functions as arguments. The first function returns the value to be observed (this.value in the example above), and the second function performs a side effect when the observed value changes (console.log('Value Changed')).

computed

A computed field is a handy way of abstracting derived state. A computed field is represented as a getter function in Javascript. This type of function takes no arguments and should have no side effects. As an example building on the code above.

class ComputedData extends Data {
    @computed
    get doubleValue() {
        return this.value * 2;
    }
}

One property of a computed field is that, when it is used in a reaction, the value of the computed field will be cached and not recomputed so long as the observables it is computed from do not change. For example:

class NewData {
    @observable
    first = 1;

    @observable
    second = 1;

    @computed
    get doubleSecond() {
        return this.second * 2;
    }

    @action
    incrementFirst = () => {
        this.first += 1;
    }

    constructor() {
        reaction(() => this.first, () => console.log(this.doubleSecond));
    }
}

const data = new NewData();

data.incrementFirst(); // The value of doubleSecond will be computed and printed to the console
data.incrementFirst(); // The cached value of doubleSecond will be printed to the console

autorun

autorun is a special type of reaction that will run some side effect based on what values are used in the side effect. For example, building on the first code snippet:

const data = new Data();

// This function will run once when the autorunner is first set up.
autorun(() => {
    console.log('The new value is ' + data.value);
});

data.incrementValue(); // This will result in the above message being printed again with the new value.

One caveat of autorun is that it can only observe values that are used. If an observable is not used in the autorun function then MobX will not know to observe it. For example:

const data = new Data();

// Create an autorunner that won't observe anything
// Even though data.value is observable, it is not used and therefore it will not be observed
autorun(() => {
    if (false) {
        console.log(data.value);
    }
});

data.incrementValue(); // No message will be printed since no observable was used in the initial running of the function.

React and MobX.

Now that we have an idea of how MobX works, we can cover how MobX integrates with React and how we can use it to build reactive user interfaces. Let’s construct a MobX powered component:

@observer
class MyFirstObserver extends React.Component {
    @observable
    value = 0;

    @action
    incrementValue = () => this.value += 1;

    render() {
        return (
            <div>
                <span>The value is {this.value}</span>
                <button onClick={this.incrementValue}>Increase Me!</button>
            </div>
        );
    }
}

You can see at the top of the component we have declared it with an @observer decorator. This means the component will re-render itself when any observable values that are used in the render function change. This means since we use this.value in the render function, the component will be automatically re-rendered when the button is clicked and invokes the action function callback.

It is important to note the semantics of an observer component is the same as autorun. If a value is not used in the first run of a render function then it will not be observed by the observer component. For example take the following render function:

render() {
    if (false) {
        return <h3>{this.value}</h3>;
    }

    return <h3>Hello world</h3>;
}

Since this.value is not actually used in the call to render then it will not be tracked and the component will not update if it is changed.

Often in our react code we use MobX observables in any case where we would use react state and generally don’t need to call setState in our code.

Use the following links to learn more about the MobX observable framework, and complete the quiz to test your knowledge.

Learn more:

On this page