Хотите заказать веб-сайт? Связаться с нами

MobX в React: гайд по управлению состоянием

MobX — одна из самых популярных альтернатив Redux для управления состоянием в React-приложениях. В этом уроке разберём, как устроена реактивность MobX, какие есть ключевые понятия — observable, action и computed, — и как связать хранилище с компонентами через observer.

MobX в React: гайд по управлению состоянием

Что такое MobX и зачем он нужен

MobX — это библиотека для управления состоянием, построенная на принципах реактивного программирования. В отличие от Redux, где данные меняются только через строго описанные редьюсеры, MobX опирается на наблюдаемые объекты: как только их значение меняется, все зависимые вычисления и компоненты автоматически обновляются сами, без ручной подписки на изменения.

В основе MobX лежат четыре ключевых понятия. observable помечает данные, за изменениями которых нужно следить. action — это функции, которые изменяют наблюдаемое состояние. computed описывает производные значения, которые пересчитываются автоматически при изменении зависимостей. А reaction и autorun запускают побочные эффекты в ответ на изменение данных, например, логирование или синхронизацию с сервером.

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

Доменные сущности можно описывать обычными классами, а не громоздкими редьюсерами, и при этом получать автоматическую реактивность «из коробки». Это делает MobX хорошим выбором для крупных приложений с богатой бизнес-логикой.

Установка и настройка MobX в React проекте

Для работы с MobX в React потребуются два пакета. mobx — это ядро библиотеки, которое не зависит от React и отвечает за саму реактивность. mobx-react-lite — лёгкие биндинги для функциональных компонентов, которые добавляют хук observer.

Если в проекте ещё остались классовые компоненты, вместо mobx-react-lite стоит подключить более полный пакет mobx-react, который поддерживает и функциональные, и классовые компоненты.

npm install mobx mobx-react-lite

Начиная с MobX 6, декораторы @observable и @action больше не обязательны — вместо них рекомендуется использовать функцию makeAutoObservable, которая автоматически определяет, какие поля сделать наблюдаемыми, какие методы — действиями, а какие геттеры — вычисляемыми значениями. Это избавляет от настройки babel-плагинов и decorators в tsconfig.json.

Основные концепции MobX

Действия — это единственный правильный способ изменять наблюдаемое состояние.

Разберём три базовых понятия MobX на примере простого хранилища счётчика.

Observable

Наблюдаемое состояние — это поля класса, изменения которых MobX отслеживает автоматически. Ниже пример хранилища, в конструкторе которого вызывается makeAutoObservable(this) — эта функция превращает поле count в наблюдаемое, метод increment — в действие, а геттер doubledCount — в вычисляемое значение.

import { makeAutoObservable } from 'mobx';
 
class CounterStore {
  count = 0;
 
  constructor() {
    makeAutoObservable(this);
  }
 
  increment() {
    this.count += 1;
  }
 
  decrement() {
    this.count -= 1;
  }
 
  get doubledCount() {
    return this.count * 2;
  }
}
 
export const counterStore = new CounterStore();

Action

Оборачивание мутаций в action позволяет MobX группировать несколько изменений в одно обновление и уведомлять подписчиков только один раз, а не после каждой отдельной мутации. При использовании makeAutoObservable все обычные методы класса автоматически становятся действиями, поэтому дополнительно оборачивать их вручную не нужно.

Computed значения

Вычисляемые значения — это геттеры, зависящие от наблюдаемых данных. MobX кеширует результат и пересчитывает его только тогда, когда реально изменились значения, от которых он зависит. Это отличается от обычной функции, которая выполнялась бы заново при каждом обращении, даже если исходные данные не менялись.

Не спешите переходить на legacy-декораторы @observable и @action — они требуют дополнительной настройки Babel или TypeScript и хуже работают с современными сборщиками. Функция makeAutoObservable покрывает большинство сценариев и делает хранилище короче и понятнее.

Использование MobX-хранилища в React-компонентах

Чтобы функциональный компонент реагировал на изменения наблюдаемых данных, его нужно обернуть в функцию observer из пакета mobx-react-lite. Без этой обёртки компонент прочитает значение observable-поля один раз при монтировании, но не перерисуется, когда оно изменится.

import { observer } from 'mobx-react-lite';
import { counterStore } from './counterStore';
 
export const Counter = observer(() => {
  return (
    <div>
      <p>Значение: {counterStore.count}</p>
      <p>Удвоенное: {counterStore.doubledCount}</p>
      <button onClick={() => counterStore.increment()}>+</button>
      <button onClick={() => counterStore.decrement()}>-</button>
    </div>
  );
});

В небольших проектах хранилище можно импортировать напрямую как синглтон, как в примере выше. Но в более крупных приложениях удобнее передавать хранилища через React Context — это упрощает тестирование и позволяет создавать отдельные экземпляры хранилищ для разных частей приложения, например, для сторибуков или тестов.

import { createContext, useContext } from 'react';
import { counterStore } from './counterStore';
 
const StoreContext = createContext(counterStore);
 
export const useStore = () => useContext(StoreContext);

Часто несколько доменных хранилищ объединяют в один RootStore, который создаётся один раз в корне приложения и раздаёт свои части через Context. Такой подход хорошо масштабируется по мере роста проекта: каждое доменное хранилище отвечает за свою область — пользователи, корзина, уведомления, — а RootStore просто их агрегирует.

MobX vs Redux — сравнение подходов

MobX и Redux решают одну и ту же задачу — управление состоянием приложения, — но делают это принципиально по-разному.

Redux строится вокруг неизменяемого состояния, чистых функций-редьюсеров и явного описания каждого изменения через action-объекты. MobX, наоборот, поощряет мутируемые наблюдаемые данные и скрывает механизм подписки на изменения от разработчика.

Критерий Особенность
Подход к состоянию MobX использует мутируемые наблюдаемые объекты, Redux — неизменяемое состояние и чистые редьюсеры
Количество кода MobX требует меньше шаблонного кода благодаря makeAutoObservable, Redux — больше кода: экшены, редьюсеры, селекторы
Реактивность MobX автоматически отслеживает зависимости и обновляет только нужные компоненты, Redux требует явных селекторов и мемоизации
Инструменты отладки Redux имеет мощные DevTools с time-travel debugging, инструменты MobX менее развиты
Кривая обучения MobX проще для новичков, Redux требует понимания принципов immutability и паттерна Flux

Выбор между MobX и Redux редко бывает однозначным. Redux остаётся стандартом для команд, которым важна предсказуемость, единый источник истины и мощные инструменты отладки. MobX выигрывает там, где важна скорость разработки, меньше шаблонного кода и удобная работа со сложными объектными моделями.

Не оборачивайте в observer весь компонент целиком, если он содержит крупные под-деревья, которые не читают наблюдаемые данные. Разбейте UI на более мелкие компоненты и оборачивайте в observer только те из них, что реально используют данные из хранилища — так MobX будет перерисовывать лишь необходимые части интерфейса.

Итоги

В этом уроке мы разобрали, что такое MobX и чем он отличается от Redux, изучили ключевые понятия — observable, action и computed, — и научились подключать хранилища к React-компонентам через observer и Context. MobX — гибкий инструмент, который стоит попробовать в проекте со сложной доменной моделью.

В следующем уроке поговорим о стилизации React-приложений — библиотеках Chakra UI, Material UI и Ant Design.

Теги: