Перейти к основному содержимому

Наблюдаемое состояние

Терминология

Прежде чем идти дальше, давайте определимся с терминами, чтобы говорить на одном языке.

MobX Principles См. https://mobx.js.org/assets/flow2.png

  • Из внешней среды к нам прилетают события (events), например клики мыши, пользовательский ввод, тики таймера и т.д.
  • Экшены (actions) это функции, которые мутируют (обновляют) наблюдаемые поля.
  • Сумма наблюдаемых полей (observable state) это стейт нашего приложения.
  • Изменение стейта приводит к обновлению вычисляемых значений (computed values) и к побочным эффектам (side-effects), которые мы будем называть реакциями.

Наблюдаемое состояние

Итак, давайте перейдем в папку /src и создадим файл counter.store.ts с кодом:

class Store {
count = 0

inc = () => {
this.count++
}

dec = () => {
this.count--
}

get double() {
return this.count * 2
}
}

Поле count - это стейт, т.е. состояние нашего приложения. Методы класса это экшены, т.е. функции, которые мутируют стейт. Инкремент inc увеличивает count на единицу, декремент dec уменьшает.

Пока что тут нет ничего от MobX, сейчас это обычный JavaScript-класс. Чтобы реактивность работала, в конструкторе класса необходимо вызвать MobX-функцию makeObservable и передать в нее сам класс и карту аннотаций вторым аргументом.

import { makeObservable, observable, action, computed } from 'mobx'

class Store {
constructor() {
makeObservable(this, {
count: observable,
inc: action,
dec: action,
double: computed,
})
}

count = 0

inc = () => {
this.count++
}

dec = () => {
this.count--
}

get double() {
return this.count * 2
}
}

Поле count мы пометили как observable, т.е. наблюдаемое поле. Использование observable подобно превращению поля объекта в ячейку электронной таблицы. Но в отличие от электронных таблиц, эти значения могут быть не только примитивами (числа, строки), но и объектами, массивами и т.д.

Функции inc и dec мы пометили как экшены.

Computeds

Геттер double помечен как computed. Computed это вычисляемые из стейта значения, т.е. это чистая функция, которая возвращает производную от нашего состояния. В данном случае мы просто умножаем значение count на два.

Одна из особенностей computed заключается в том, что он запоминает вычисленный результат. То есть, когда вычисление завершено, MobX сравнивает новый результат с предыдущим. Если результат совпадает, то уведомление наблюдателям не будет отправлено.

Декораторы

В старых версиях MobX декораторы были рекомендуемым синтаксисом для написания классов. Во многих статьях и руководствах используется такое написание, поэтому с ним нужно быть знакомым.

import { makeObservable, observable, action, computed } from 'mobx'

class Store {
constructor() {
makeObservable(this)
}

@observable count = 0

@action inc = () => {
this.count++
}

@action dec = () => {
this.count--
}

@computed get double() {
return this.count * 2
}
}

Вместо передачи карты аннотаций в makeObservable, мы декорируем поля и методы классы по месту их объявления. Если мы используем такой способ, то должны включить флаг для поддержки экспериментальных декораторов в файле tsconfig.json:

"experimentalDecorators": true

makeAutoObservable

В принципе можете выбирать любой способ декорирования какой вам больше нравится. На практике чаще всего используется makeAutoObservable.

import { makeAutoObservable } from 'mobx'

class Store {
constructor() {
makeAutoObservable(this)
}

count = 0

inc = () => {
this.count++
}

dec = () => {
this.count--
}

get double() {
return this.count * 2
}
}

makeAutoObservable автоматически помечает все поля класса как наблюдаемые. Все методы класса помечаются как экшены, а все геттеры как computed.