MobX Basics

Yiliang Wang

Reactive programming in React

view = f(state)

Once     is defined, each time             changes,            will update automatically

f

state

view

render function

props + useState()

React tracks each setState() call

useState() from ancestors

Reactive programming in MobX React

view = f(state)

Once     is defined, each time             changes,            will update automatically.

f

state

view

render function

props + useState() + observables

useState() from ancestors

  1. React tracks each setState() call
  2. Mobx tracks read/write of observables

MobX core concepts

Observables

                         mutable state                       

Read

Write

observable

Collect dependencies

Trigger updates

const foo = {
    value: 0
};

makeAutoObserverable(foo);

// read foo.value
const value = foo.value;
console.log(value);

// write foo.value
foo.value = 1;

MobX core concepts

Reactions

Consumer functions of observables, which automatically run whenever relevant observables change.

const foo = {
    value: 0
};

makeAutoObservable(foo);

// register a reaction which uses foo.value
autorun(() => {
  const value = foo.value;
  console.log(value);
});

// write foo.value
window.setInterval(() => {
  foo.value++;
}, 1000);

MobX core concepts

Actions

An action is a function that modifies the state.

const foo = {
    value: 0
};

makeAutoObservable(foo);

// register a reaction which uses foo.value
autorun(() => {
  const value = foo.value;
  console.log(value);
});

// foo.value = 1;
// foo.value = 2;

const updateValue = action(() => {
  foo.value = 1;
  foo.value = 2;
});

updateValue();
  1. They are run inside transactions
    1. ​Avoid illegal intermediate state
    2. Better performance
  2. This helps to clearly identify where the state updates happen

Why using actions?

Creating observables

  • makeAutoObservable(target, overrides?, options?)
import { makeAutoObservable  } from "mobx";

class Timer {
    secondsPassed = 0;

    constructor() {
        makeAutoObservable(this);
    }

    increaseTimer() {
        this.secondsPassed += 1;
    }
}


const timer = new Timer();
import { makeAutoObservable } from "mobx";

function createTimer() {
    return makeAutoObservable({
        secondsPassed: 0,
        increment() {
            this.value += 1;
        }
    })
}

const timer = createTimer();

React integration

import { observer } from "mobx-react-lite"

const MyComponent = observer(props => ReactElement);

Using external state in observer components

import { observer } from "mobx-react-lite";

const myTimer = new Timer();

const TimerView = observer(({ timer }) => <span>Seconds passed: {timer.secondsPassed}</span>);

// Pass myTimer as a prop.
ReactDOM.render(<TimerView timer={myTimer} />, document.body);

Option 1: observables can be passed into components as props

Using external state in observer components

import { observer } from "mobx-react-lite";

const myTimer = new Timer();
// or import myTimer from "../store/my-timer";

// No props, `myTimer` is directly consumed from the closure.
const TimerView = observer(() => <span>Seconds passed: {myTimer.secondsPassed}</span>);

ReactDOM.render(<TimerView />, document.body);

Option 2: using global variables

Using external state in observer components

import {observer} from 'mobx-react-lite';
import {createContext, useContext} from "react";

const TimerContext = createContext<Timer>();

const TimerView = observer(() => {
    // Grab the timer from the context.
    const timer = useContext(TimerContext);
    return (
        <span>Seconds passed: {timer.secondsPassed}</span>
    );
});

ReactDOM.render(
    <TimerContext.Provider value={new Timer()}
        <TimerView />
    </TimerContext.Provider>,
    document.body
);

Option 3: using React context

🥱To be continued

Mobx Basics

By yiliang_wang

Mobx Basics

  • 52