Runinaction mobx что это

Полная реактивность: подробное объяснение работы MobX

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

В связи с большим спросом (и для того, чтобы рассказать крутую историю своим внукам) написал статью о внутренней работе MobX. Многих удивляет последовательность и скорость его работы. Но будьте уверены, здесь нет никакой магии!

Сначала давайте определимся с основными терминами:

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

В любом случае, вот все четыре понятия в маленьком примере, использующем MobX и React:

Мы можем нарисовать дерево зависимостей на основе приведенного выше списка. Интуитивно это будет выглядеть следующим образом:

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Состояние этих приложений отображается в наблюдаемых свойствах (синий цвет). Зеленое вычисленное значение fullName может быть выведено из состояния автоматически путем наблюдения за именем и фамилией. Аналогично рендеринг profileView может быть получен из nickName и fullName. profileView будет реагировать на изменения состояния, создавая побочный эффект: он обновляет дерево компонентов React.

При использовании MobX дерево зависимостей имеет минимальный размер. Например, как только человек получает псевдоним, вывод значения fullName, первого имени или фамилии больше не будет влиять на рендеринг (см. рисунок 1). Все отношения наблюдателя между этими значениями могут быть очищены, и MobX автоматически упростит дерево зависимостей соответственно:

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Reacting to state changes is always better then acting on state changes.

Любое императивное действие, которое приложение делает в ответ на изменение состояния, обычно создает или обновляет некоторые значения. Другими словами, большинство действий управляют локальным кэшем. Запуск обновления пользовательского интерфейса? Обновление агрегированных значений? Все это можно считать как замаскированная инвалидация кэша. Чтобы обеспечить синхронизацию этого кэша, вам нужно подписаться на будущие изменения состояния, которые позволят вам снова выполнить нужные действия.

Но работа с подписками (или курсорами, линзами, селекторами, коннекторами и т.д.) имеет фундаментальную проблему: по мере развития вашего приложения вы будете совершать ошибки в управлении этими подписками и либо переподписываться (продолжать подписку или сохранять значения, которые больше не используются в компоненте), либо отказываться от подписки (забывая слушать обновления).

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

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Изображение выше является хорошим примером непоследовательности интерфейса Twitter. Как я уже говорил в своем докладе Reactive2015, для этого могут быть только две причины: или отсутствует подписка у твитов, которая обновляет их при изменении данных об авторе. Или данные были нормализованы, и автор твита даже не имеет отношения к профилю текущего вошедшего в систему пользователя, несмотря на то, что обе части данных пытаются описать одни и те же свойства одного и того же человека.

Крупные подписки, в стиле Flux, очень чувствительны к переподписке (oversubscribing). Используя React, вы можете просто определить, переписываются ли ваши компоненты, логируя ненужные рендеринги. MobX уменьшит это число до нуля. Идея проста, но не интуитивна: Увеличение числа подписок приводит к меньшему числу пересчетов. MobX управляет многими тысячами наблюдателей за вас. Вы можете эффективно обменять память на циклы процессора.

Обратите внимание, что переподписка также существует в очень интересных формах. Если вы подписываетесь на данные, которые используются, но не при всех условиях, вы все равно подписываетесь сверх нормы. Например, если компонент profileView подписывается на полное имя человека с nickName, то это переподписка (см. рисунок 1). Так что важным принципом дизайна MobX является принцип:

Минимальный, согласованный набор подписок можно получить только в случае, если подписки определены в рантайме.

Вторая важная идея за MobX заключается в том, что для любого приложения, более сложного, чем TodoMVC, вам часто нужен граф данных, вместо нормализованного дерева, чтобы хранить его мысленно управляемым, но оптимальным образом. Графики обеспечивают согласованность ссылок и исключают дублирование данных, что позволяет гарантировать, что получаемые значения никогда не будут устаревшими.

Как MobX эффективно поддерживает все вычисления в согласованном состоянии

Решение: не кэшировать, а вычислять. Люди спрашивают: “Разве это не слишком дорого?”. Нет, на самом деле это очень эффективно! Причина этого, как объяснялось выше: MobX не выполняет все вычисления, но обеспечивает синхронизацию с наблюдаемым состоянием только вычисленных значений, участвующих в той или иной реакции. Эти производные называются реактивными. Чтобы снова провести параллель с электронными таблицами: при изменении одной из наблюдаемых ячеек данных необходимо пересчитывать только те формулы, которые в данный момент видны или косвенно используются рассматриваемой формулой.

Ленивое вычисление против реактивного вычисления.
Так что насчет вычислений, которые не используются прямо или косвенно реакцией? Вы все еще можете в любое время проверить значение вычисленного значения, например fullName. Решение простое: если вычисленное значение не является реактивным, оно будет вычисляться по требованию (лениво), как и обычная функция-геттер. Ленивые производные (которые никогда ничего не наблюдают) могут быть просто мусором, если они выходят за рамки видимости. Помните, что вычисляемые значения всегда должны быть чистыми функциями наблюдаемого состояния приложения? Вот почему: Для чистых функций не имеет значения, лениво они выполняются или сразу: выполнение чистой функции всегда дает один и тот же результат при одном и том же наблюдаемом состоянии.

Выполнение вычислений
Реакции и вычисленные значения выполняются MobX одним и тем же способом. При повторном запуске вычислений функция добавляется в стек вычислений: стек функций, в котором в данный момент выполняются вычисления. Пока вычисления выполняются, каждый наблюдаемый объект, к которому осуществляется доступ, регистрируется как зависимость от самой верхней функции вычислительного стека. Если требуется значение вычисленной величины, то она может быть последней известной величиной, если вычисленная величина уже находится в реактивном состоянии. Иначе он будет толкать себя на производный стек, переключаться в реактивный режим, а также запускать вычисления.

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Распространение изменения состояния

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

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

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

Синхронное выполнение

Люди часто удивляются, что MobX работает все синхронно (как RxJ и в отличие от knockout). Это имеет два больших преимущества: Первым делом становится просто невозможным когда-либо наблюдать за устаревшими производными. Таким образом, производная величина может быть использована сразу же после изменения величины, которая влияет на нее. Во-вторых, это упрощает отладку и стэк-трейсинг, так как позволяет избежать бесполезных стэк-трейсов, типичных для библиотек Promise / async.

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

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

Источник

React + mobx путь с нуля. Mobx + react, взгляд со стороны

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Кроме того, для работы с декораторами и трансформации свойств классов нам потребуются babel плагины babel-plugin-transform-class-properties и babel-plugin-transform-decorators-legacy:

У нас есть компонента Menu, давайте продолжим работу с ней. У панели будет два состояния «открыта/закрыта», а управлять состоянием будем с помощью mobx.

1. Первым делом нам нужно определить состояние и сделать его наблюдаемым посредством добавления декоратора @observable. Состояние может быть представлено любой структурой данных: объектами, массивами, классами и прочими. Создадим хранилище для меню (menu-store.js) в директории stores.

Стор представляет собой ES6 class с единственным свойством show. Мы повесили на него декоратор @observable, тем самым сказали mobx-у наблюдать за ним. Show — это состояние нашей панели, которое мы будем менять.

2. Создать представление, реагирующее на изменение состояния. Хорошо, что у нас уже оно есть, это component/menu/index.js. Теперь, когда состояние будет изменяться, наше меню будет автоматически перересовываться, при этом mobx найдет кротчайший путь для обновления представления. Что бы это произошло, нужно обернуть функцию, описывающую react компонент, в observer.

В любом react приложении нам понадобится утилита classnames для работы с className. Раньше она входила в пакет react-а, но теперь ставится отдельно:

c её помощью можно склеивать имена классов, используя различные условия, незаменимая вещь.
Видно, что мы добавляем класс «active», если значение состояние меню show === true. Если в конструкторе хранилища поменять состояние на this.show = true, то у панели появится «active» класс.

3. Осталось изменить состояние. Добавим событие click для «гамбургера» в

Note: По дефолту мы экспортируем хранилище как инстанс синглтона, также экспортируется и класс напрямую, так как он тоже может понадобиться, например, для тестов.

Для наглядности добавим стили:

И проверим, что по клику на иконку, наша панель открывается и закрывается. Мы написали минимальный mobx store для управления состоянием панели. Давайте немного нарастим мяса и попробуем управлять панелью из другого компонента. Нам потребуются дополнительные методы для открытия и закрытия панели:

Можно заметить, что мы добавили computed и action декораторы, они обязательны только в strict mode (по умолчанию отключено). Computed значения будут автоматически пересчитаны при изменении соответствующих данных. Рекомендуется использовать action, это поможет лучше структурировать приложение и оптимизировать производительность. Как видно, первым аргументом мы задаём расширенное название производимого действия. Теперь при деббаге мы сможем наблюдать, какой метод был вызван и как менялось состояние.

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Note: При разработке удобно использовать расширения хрома для mobx и react, а так же react-mobx devtools

Создадим еще один компонент

Внутри пара кнопок, которые будут открывать и закрывать панель. Этот компонент добавим на Home страницу. Должно получиться следующее:

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

В браузере это будет выглядеть так:

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Теперь мы можем управлять состоянием панели не только из самой панели, но и из другого компонента.
Note: если несколько раз произвести одно и тоже действие, например, нажать кнопку «close left panel», то в деббагере можно видеть, что экшен срабатывает, но никакой реакции не происходит. Это значит, что mobx не перересовывает компонент, так как состояние не изменилось и нам не нужно писать «лишний» код, как для pure react компонент.

Осталось немного причесать наш подход, работать со сторами приятно, но разбрасывать импорты хранилищ по всему проекту некрасиво. В mobx-react для таких целей появился Provider (см. Provider and inject) — компонент, который позволяет передавать сторы (и не только) потомкам, используя react context. Для этого обернем корневой компонент app.js в Provider:

Тут же импортируем все сторы (у нас один) и передаём их провайдеру через props. Так как провайдер работает с контекстом, то сторы будут доступны в любом дочернем компоненте. Также разобьем menu.js компонент на два, чтобы получился «глупый» и «умный» компонент.

«Глупый» нам не интересен, так как это обычный stateless компонент, который получает через props данные о том открыта или закрыта панель и колбэк для переключения.

Гораздо интереснее посмотреть на его враппер: мы видим тут HOC, где мы инжектим необходимые сторы, в нашем случае «leftMenuStore», в качестве компонента мы передаем наш «глупый компонент», обернутый в observer. Так как мы приинжектили leftMenuStore, то хранилище теперь доступно через props.

практически тоже самое мы проделываем с left-panel-controller:

С той лишь разницей, что тут мы не используем observer, так как для этого компонента перерисовавать ничего не требуется, от хранилища нам нужны лишь методы openLeftPanel() и closeLeftPanel().

Note: я использую displayName для задания имени компоненту, это удобно для деббага:

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Это все просто, теперь давайте получим данные с сервера, пусть это будет список пользователей с чекбоксами.

Идем на сервер и добавляем роут «/users» для получения пользователей:

Нарочно добавим задержку, чтобы проверить, что приложение работает корректно даже с большим интервалом ответа сервера.

Далее нам понадобится

Тут описан класс User со свойством user. В mobx есть observable.map тип данных, он как раз подойдет нам для описания user-а. Грубо говоря, мы получаем наблюдаемый объект, причем, наблюдать можно за изменением конкретного поля. Также становятся доступны getter, setter и прочие вспомогательные методы. Например, в конструкторе с помощью «merge», мы легко можем скопировать поля из userData в user. Это очень удобно, если объект содержит много полей. Также напишем один action для переключения состояния пользователя и вычисляемое значения для получения информации о пользователе.

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

Note: autorun выполняет функцию автоматически, если наблюдаемое значение было изменено. Для примера, тут выводится все пользователи в консоль. Если попробовать достать пользователей методом «getUsers()», то можно заметить, что тип возвращаемых данных не Array, а ObservableArray. Для конвертации observable объектов в javascript структуру, используем toJS().

В app.js не забудем дописать новый user-store, чтобы потомки могли им пользоваться.

Добавим react компоненты в директорию components:

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

Показываем список пользователей и информацию по их количеству. Передаём «toggle()» метод стора через props.

Рендерим одного пользователя.

Добавляем стили и цепляем готовый компонент на Home страницу. Все готово(github), можно поиграть с чекбоксами и убедиться, что все методы работают.

В итоге мы увидели как работает mobx в связке с react-ом, учитывая все возможности mobx, можно предположить, что такое решение имеет право на жизнь. Mobx прекрасно справляется с обязанностью менеджера состояний для react приложений и предоставляет богатый функционал для реализации.

Источник

Опыт использования MobX в большом приложении

Runinaction mobx что это. Смотреть фото Runinaction mobx что это. Смотреть картинку Runinaction mobx что это. Картинка про Runinaction mobx что это. Фото Runinaction mobx что это

Меня зовут Сергей, я работаю в команде разработки приложений контроля качества Tinkoff.
Поделюсь опытом нашей команды в использовании библиотеки Mobx и расскажу о деталях работы с ней в связке с React. В этой статье не будет описания базовых концепций. Я расскажу о вещах, которые мы отметили для себя за время разработки и считаем полезными для всех, кто решил использовать Mobx в своем проекте.

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

Сказ об удобстве

В статьях и комментариях о Mobx часто встречается слово «магия». Это связано с непониманием того, как Mobx обновляет UI-компоненты.

На проектах с Redux, как правило, используют PureCompoment, хуки и shouldComponentUpdate для предотвращения лишних перерисовок. Mobx же автоматически переопределяет shouldComponentUpdate и использует мемоизацию для компонент. Скрытые под капотом оптимизации смущают разработчиков, а смущение вызывает недоверие. Чтобы понять, как работает MobX с React компонентами, я расскажу о проблеме, с которой мы столкнулись при использовании собственного HOC (Higher-Order Component), отвечающего за необходимость отрисовки компонента.

Допустим, HOC называется withVisible и добавляет передаваемому компоненту дополнительный булевый параметр visible. Отрисовка компонента вызывается, если передать этот параметр со значением true, в противном случае возвращаем null:

Однако использование этого HOC несет за собой побочный эффект в виде излишних перерисовок. Проблема возникает из-за того, что вызывается ререндер компонента, отображение которого не зависит напрямую от значений observable-полей. Давайте взглянем на пример использования этого HOC:

Получается довольно простой и читаемый код. Но тут есть сразу несколько проблем. Уже видите их?

Передаваемая модель содержит три наблюдаемых значения: hasAudio, isLoading и hasErrors. Чтобы компонент обновлялся после изменения этих полей, используется функция observer из библиотеки mobx-react. Она подписывает компонент на обновления всех observable. Отметим, что эти поля напрямую не влияют на отрисовку компонента Player, но используются для отображения дочерних компонентов. Из-за HOC мы получили дополнительную перерисовку компонента на уровень выше того, состояние которого обновляется.

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

В примере с кодом присутствует и другая проблема, которая не связана с HOC. Давайте взглянем на вот эту строчку:

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

Чтобы не попадать на такие ошибки стоит придерживаться базового принципа, который советует создатель библиотеки Michel Weststrate.

Базовый принцип

Базовый принцип гласит: делайте компоненты как можно меньше и используйте computed-значения.

Давайте разбираться. С разбиением на маленькие компоненты вроде бы все понятно: чем меньше компоненты, тем точечней будут перерисовки. Но зачем computed-значения? А они как раз помогают избежать ситуации, которая описана в проблеме выше:

Computed помогают избегать лишних перерисовок. По сути это новое observable-поле, которое не хранит значение внутри себя, а вычисляет его на основе других observable-полей. Для компонентов оно будет выглядеть как еще одно observable-поле, но обновляться будет только при изменении результата выполнения выражения внутри себя. Тут может возникнуть справедливый вопрос: а как это работает?

Computed автоматически вычисляется, если изменяется какое-либо observable-значение, влияющее на него. При изменении hasAudio или hasErrors showAudio получит уведомление и выполнит пересчет собственного значения. Возвращаемое значение кэшируется и не будет изменено, пока не поступит новое уведомление об изменении. То есть вычисления происходят не при каждом обращении к computed, а когда изменяются зависимые observable-поля.

Отметим, что автоматическое вычисление не происходит, если у поля нет слушателей или реакций, использующих его. В свою очередь computed является эквивалентом observable и может быть использован в других computed-полях. Рекомендуется использовать как можно чаще для оптимизации количества перерисовок.

С базовым принципом более-менее понятно. Дальше я поделюсь методиками, которые редко упоминают в других статьях или используют в проектах (по крайней мере те, что я видел), но стоят того, чтобы о них сказать.

А всегда ли нужен хук useCallback?

В классической архитектуре большого приложения, в котором используется React в связке с MobX, можно выделить три основных слоя:

Пользователь взаимодействует с UI-представлением. Данные и состояние приложения хранятся в моделях. Сервисы используются для выполнения бизнес-логики, вызова API-бэкэнда и передачи результатов запросов в модели. UI-представление взаимодействует с остальными слоями при помощи коллбеков.

Рассмотрим пример. Компонент Button принимает в параметр onClick коллбек doSomething из модели Model.

Передать коллбек напрямую нельзя, теряется контекст. Чтобы решить эту проблему, разработчики пользуются тремя стандартными способами.

Первый способ очень распространен в различных примерах с кодом и выглядит примерно так:

Этот способ не оптимален. Использование стрелочной функции в render-методе приводит к созданию новой функции при каждом обновлении компонента. Button будет перерисовываться при каждом изменении родительского компонента из-за изменения ссылки в параметре onClick. Это нарушает оптимизации приложения и влияет на его производительность.

Второй способ — сделать компонент классовым и вынести коллбек-функцию в отдельный метод класса при помощи стрелочной функции.

Это стандартное решение. При передаче метода дочернему компоненту сохраняется контекст, но при этом функция не изменяет ссылку при обновлении компонента.

C появлением хуков в React 16.8 появился еще один способ решения проблемы. Переделывать функциональный компонент на классовый из-за одного маленького коллбека перестало быть необходимым. Можно воспользоваться хуком useCallback:

Этот хук возвращает мемоизированную версию коллбека и обновится, только если изменятся зависимости, переданные вторым параметром в виде массива. Контекст сохраняется так же, как и в предыдущем способе, но компонент остается функциональным.

При использовании @action.bound код будет выглядеть примерно так:

Заметим, что использование декоратора @action.bound делает код более компактным, но не эквивалентно применению хука useCallback. Не используйте его, если внутри коллбека нет изменения наблюдаемых полей.

Строгость наше всё

Как мы знаем, в MobX есть наблюдаемые (observable) переменные и action-функции, которые предназначены для изменения наблюдаемых переменных. Изменять observable-поля только из action-функций — хорошая практика, но не обязательная. Чтобы включить строгий режим для всего приложения, в mobx есть настройка enforceActions. Ее достаточно указать в корне проекта.

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

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

Первый вариант для устранения этой проблемы — использовать runInAction-функцию. Она является надстройкой для action и позволяет не выносить всю логику обратного вызова в отдельный action-метод. Достаточно выделить только ту часть, которая влияет на состояние в коллбеке асинхронной функции:

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

В библиотеке Mobx есть функция flow для работы с асинхронными action-функциями. Про нее можно прочитать тут, но я думаю, что она не подходит для многих проектов. Если не вдаваться в подробности, то ее основной минус в том, что работает только с генераторами. Согласитесь, далеко не во всех проектах используются генераторы и хочется писать всем привычные async-функции и промисы. К сожалению, в Mobx других альтернатив нет, но у нас на проекте было придумано свое решение.

Для нашего приложения мы используем babel, и именно он позволил решить проблему с асинхронными функциями. Для него есть очень полезный плагин @babel/plugin-transform-async-to-generator, который при компиляции исходного JS-кода меняет все асинхронные функции на генераторы. А ведь с генераторами mobx умеет работать, осталось только это связать:

В примере показана упрощенная настройка плагина для babel, которая заменяет каждую асинхронную функцию в генератор и оборачивает результат в mobx.flow. Однако такая замена может быть избыточной. Далеко не каждая асинхронная функция будет содержать логику, при которой происходит изменение observable-полей. Чтобы подсказывать babel-плагину о необходимости трансформации, в нашем проекте дополнительно сделан кастомный декоратор. Babel-плагин видит его и понимает, что обернутую асинхронную функцию необходимо трансформировать в генератор и дополнительно обернуть результат в mobx.flow-функцию.

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

Обзор будущего Mobx

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

Как видим, основная причина — в поддержке работы декораторов. Они не внесены в стандарт современного джаваскрипта и требуют дополнительных настроек при использовании. Отказ от декораторов позволит сократить размер библиотеки и упростит документацию к ней за счет сокращения вариантов использования. Когда декораторы стандартизируют, обратное их добавление не приведет к критическим изменениям.

Отметим, что у нас в проекте декораторы используются не только в связке с MobX, но и для Dependency Injection. Хоть декораторы и не входят в стандарт джаваскрипта, все-таки они используются во многих библиотеках для простоты и наглядности кода. Ярким примером является Angular и NestJS. Большинство комментаторов в обсуждении к пропозалу пока скептически относятся к удалению декораторов и высказываются за возможность оставить декораторы или вынести их в отдельную библиотеку.

Еще одно интересное изменение, о котором стоит рассказать в контексте этой статьи: теперь строгий режим будет включен по умолчанию. Как и говорилось в предыдущем разделе, изменение observable-значений только из action-функций — хорошая практика. Если вы столкнетесь с проблемой асинхронных функций, то, надеюсь, статья будет полезна в решении этой проблемы.

Mobx — очень мощный инструмент, который подходит для больших проектов и легок для старта. Но нужно следить за тем, как вы с ним работаете. Используйте computed-значения как можно чаще, делайте компоненты как можно меньше. Старайтесь инжектить данные по месту их использования.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *