State Management

MobX: reactions

Черновик письма должен сам сохраняться в localStorage при каждом изменении текста. Фильтры в поиске должны слать запрос только когда меняется сама строка фильтра, а не любое поле стора. Модалка приветствия должна показаться один раз, когда профиль наконец загрузился, и больше не возвращаться. Три разных побочных эффекта на изменение состояния, и в MobX под каждый есть своя реакция: autorun, reaction и when.

  • Автосохранение черновиков и настроек в localStorage при изменении состояния
  • Запрос к серверу при смене фильтров, сортировки или строки поиска
  • Логирование и аналитика на изменение конкретных полей домена
  • Однократные эффекты по условию: показать онбординг, когда данные загрузились
  • Синхронизация observable-стора с внешним миром: URL, заголовок вкладки, вебсокет

Предварительные знания

  • observable, computed и action из предыдущего урока MobX
  • Идея автоматического отслеживания зависимостей по прочитанным полям
  • Понятие побочного эффекта и необходимости его очистки
  • MobX: observable, computed, action

autorun: эффект на любое прочитанное изменение

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

Эффект читает draft.text, поэтому autorun подписался на это поле. При каждом setText текст пишется в localStorage. autorun возвращает функцию-disposer: вызов dispose() останавливает наблюдение, и эффект больше не запускается. Без вызова disposer реакция живёт, пока живёт приложение, и это частый источник утечек.

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

Как autorun определяет, на какие изменения реагировать?

reaction: разделение данных и эффекта

reaction принимает две функции. Первая, data-функция, возвращает значение, за которым нужно следить. Вторая, эффект, получает это значение и выполняется только когда оно изменилось. В отличие от autorun, эффект не запускается сразу и не зависит от всего прочитанного: отслеживается ровно то, что вернула data-функция.

Здесь эффект бежит только при изменении search.query. Смена search.page реакцию не трогает, потому что page не возвращается из data-функции. Если бы тот же эффект написали через autorun и внутри прочитали и query, и page, запрос летел бы и при смене страницы. reaction даёт точный контроль над тем, что именно служит триггером.

  • autorun — Запускается сразу и реагирует на всё прочитанное внутри. Удобно для эффекта, который зависит от многих полей сразу
  • reaction — Не запускается сразу, отслеживает только результат data-функции и даёт в эффект старое и новое значение. Точный триггер

Эффект reaction получает не только новое значение, но и предыдущее вторым аргументом. Это удобно, когда реакция зависит от направления изменения: например, слать запрос только если строка действительно стала длиннее, а не просто изменилась.

Чем reaction отличается от autorun?

when: однократный эффект по условию

when ждёт, пока заданное условие станет истинным. Как только predicate-функция вернёт true, when выполняет эффект один раз и автоматически останавливает наблюдение. Это реакция для одноразовых сценариев: дождаться загрузки данных, дождаться готовности соединения, показать что-то ровно один раз.

Здесь модалка покажется ровно один раз, когда profile.loaded станет true, и больше when наблюдать не будет: он сам отписался. Не нужно вручную вызывать disposer для штатного завершения, хотя его всё равно возвращают на случай, если эффект нужно отменить до выполнения условия. У when есть и промис-форма без второго аргумента, которую можно дождаться через await.

Сводка выбора: autorun когда эффект зависит от многих полей и должен бежать сразу и при каждом их изменении. reaction когда триггером служит одно конкретное значение и сразу запускать не нужно. when когда эффект одноразовый и привязан к достижению условия. Все три это про побочные эффекты, а чистые производные значения остаются за computed.

Нужно показать онбординг ровно один раз, как только профиль загрузится, и больше за этим не следить. Какая реакция подходит лучше всего?

Связь с другими темами

Этот урок про побочные эффекты на изменение состояния. Рядом стоят соседи:

  • MobX: observable, computed, action — Реакции наблюдают именно за observable и computed, которые разбирались в предыдущем уроке
  • MobX: архитектура — Реакции мостят доменные сторы с эффектами: персистентностью, логами, синхронизацией

Итог

  • Реакции это побочные эффекты, которые MobX запускает при изменении наблюдаемого состояния
  • autorun запускается сразу и затем при каждом изменении любого observable, прочитанного внутри
  • reaction разделяет отслеживаемые данные и эффект: эффект бежит только при изменении значения из data-функции
  • when ждёт, пока условие станет истинным, выполняет эффект один раз и автоматически отписывается
  • Каждая реакция возвращает функцию-disposer, и её нужно вызвать, чтобы остановить наблюдение
  • Реакции для побочных эффектов, computed для чистых производных значений: их не смешивают

Связанные уроки

  • sm-22-mobx-observables — Реакции запускаются на изменение observable и computed, поэтому их механика нужна заранее
  • sm-24-mobx-architecture — В архитектуре реакции связывают доменные сторы с эффектами вроде сохранения и логирования
MobX: reactions

0

1

Войти