Наблюдаемое состояние
Терминология
Прежде чем идти дальше, давайте определимся с терминами, чтобы говорить на одном языке.
См. 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
.