Настройка State Management (MobX) для React-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка State Management (MobX) для React-приложения
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Настройка State Management (MobX) для React-приложения

MobX использует реактивное программирование: наблюдаемые (observable) данные, вычисляемые значения (computed) и реакции (autorun, reaction, when). Компоненты, обёрнутые в observer, автоматически подписываются на ровно те наблюдаемые, которые читают при рендере — не больше.

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

Что входит в работу

Проектирование стор-классов, настройка декораторов или makeObservable/makeAutoObservable, интеграция с React через mobx-react-lite, реакции и сайд-эффекты, DevTools, персистентность, тестирование сторов.

Установка

npm install mobx mobx-react-lite

При использовании TypeScript с декораторами нужно включить в tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "useDefineForClassFields": false
  }
}

Стор через makeAutoObservable

Самый лаконичный способ — makeAutoObservable. MobX сам определяет, что наблюдаемое, что вычисляемое, что экшен:

import { makeAutoObservable, runInAction } from 'mobx'

class CartStore {
  items: CartItem[] = []
  loading = false
  error: string | null = null

  constructor() {
    makeAutoObservable(this)
  }

  // computed
  get total() {
    return this.items.reduce((sum, i) => sum + i.price * i.quantity, 0)
  }

  get count() {
    return this.items.reduce((sum, i) => sum + i.quantity, 0)
  }

  // action
  addItem(product: Product) {
    const existing = this.items.find((i) => i.id === product.id)
    if (existing) {
      existing.quantity++
    } else {
      this.items.push({ ...product, quantity: 1 })
    }
  }

  removeItem(id: string) {
    this.items = this.items.filter((i) => i.id !== id)
  }

  clearCart() {
    this.items = []
  }

  // async action — мутации внутри runInAction
  async checkout() {
    this.loading = true
    this.error = null
    try {
      await api.post('/orders', { items: this.items })
      runInAction(() => {
        this.items = []
        this.loading = false
      })
    } catch (err) {
      runInAction(() => {
        this.error = err instanceof Error ? err.message : 'Ошибка оформления'
        this.loading = false
      })
    }
  }
}

export const cartStore = new CartStore()

Декораторы (альтернативный синтаксис)

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

class UserStore {
  @observable user: User | null = null
  @observable token: string | null = null

  constructor() {
    makeObservable(this)
  }

  @computed get isAuthenticated() {
    return this.token !== null
  }

  @action setUser(user: User, token: string) {
    this.user = user
    this.token = token
  }

  @action logout() {
    this.user = null
    this.token = null
  }
}

Интеграция с React через Context

import { createContext, useContext } from 'react'

interface RootStore {
  cart: CartStore
  user: UserStore
  ui: UIStore
}

const rootStore: RootStore = {
  cart: new CartStore(),
  user: new UserStore(),
  ui: new UIStore(),
}

const StoreContext = createContext<RootStore>(rootStore)

export const StoreProvider = ({ children }: { children: React.ReactNode }) => (
  <StoreContext.Provider value={rootStore}>{children}</StoreContext.Provider>
)

export const useStore = () => useContext(StoreContext)
import { observer } from 'mobx-react-lite'

const CartButton = observer(() => {
  const { cart } = useStore()

  return (
    <button onClick={() => cart.checkout()} disabled={cart.loading}>
      {cart.loading ? 'Оформление...' : `Оформить (${cart.count} шт, ${cart.total} ₽)`}
    </button>
  )
})

observer — HOC из mobx-react-lite, который превращает компонент в реактивный. Без него MobX-состояние не будет вызывать ре-рендер.

Реакции и сайд-эффекты

import { autorun, reaction, when } from 'mobx'

// autorun: запускается немедленно и при каждом изменении зависимостей
const dispose = autorun(() => {
  document.title = `Корзина (${cartStore.count})`
})

// reaction: следит за первой функцией, запускает вторую при изменении
reaction(
  () => userStore.token,
  (token) => {
    if (token) {
      localStorage.setItem('auth_token', token)
    } else {
      localStorage.removeItem('auth_token')
    }
  }
)

// when: однократно выполняется, когда условие становится true
when(
  () => cartStore.count > 10,
  () => notificationStore.show('Много товаров — оформите заказ!')
)

Все три возвращают dispose-функцию — важно вызывать её при размонтировании компонента или уничтожении стора.

Персистентность через makePersistable

npm install mobx-persist-store
import { makePersistable } from 'mobx-persist-store'

class ThemeStore {
  theme: 'light' | 'dark' = 'light'
  language = 'ru'

  constructor() {
    makeAutoObservable(this)
    makePersistable(this, {
      name: 'ThemeStore',
      properties: ['theme', 'language'],
      storage: window.localStorage,
    })
  }

  setTheme(theme: 'light' | 'dark') {
    this.theme = theme
  }
}

MobX DevTools

npm install mobx-devtools-mst  # или используем встроенный spy

Для отладки без дополнительных пакетов:

import { spy } from 'mobx'

if (process.env.NODE_ENV === 'development') {
  spy((event) => {
    if (event.type === 'action') {
      console.log(`[MobX] ${event.name}`, event.arguments)
    }
  })
}

Тестирование

MobX-сторы — обычные классы, тестируются без React:

import { CartStore } from '../stores/CartStore'

describe('CartStore', () => {
  let store: CartStore

  beforeEach(() => {
    store = new CartStore()
  })

  it('корректно считает total', () => {
    store.addItem({ id: '1', name: 'Test', price: 100 })
    store.addItem({ id: '1', name: 'Test', price: 100 })
    expect(store.count).toBe(2)
    expect(store.total).toBe(200)
  })

  it('clearCart сбрасывает всё', () => {
    store.addItem({ id: '1', name: 'Test', price: 100 })
    store.clearCart()
    expect(store.items).toHaveLength(0)
    expect(store.total).toBe(0)
  })
})

Структура проекта

src/
  stores/
    CartStore.ts
    UserStore.ts
    UIStore.ts
    RootStore.ts        # агрегирует все сторы
  context/
    StoreContext.tsx
  hooks/
    useStore.ts

Особенности и типичные ошибки

Мутировать state вне action в strict mode нельзя — MobX выбросит предупреждение. Включаем strict mode явно:

import { configure } from 'mobx'

configure({
  enforceActions: 'always',
  computedRequiresReaction: true,
})

computedRequiresReaction: true запрещает читать computed-значения вне реактивного контекста — помогает поймать случайные обращения к стору в не-observer компонентах.

Дочерние компоненты должны быть observer независимо от родителя — реактивность не наследуется автоматически.

Что делаем

Проектируем иерархию сторов, настраиваем strict mode и конфигурацию MobX, интегрируем с React через Context, настраиваем реакции для сайд-эффектов (синхронизация с localStorage, аналитика, заголовок страницы), покрываем тестами бизнес-логику в сторах.

Срок: 2–4 дня в зависимости от сложности предметной области.