Pull to refresh

Comments 15

PinnedPinned comments

К Mobx рекомендую пакет, автоматический оборачивающий компоненты в observer:  https://github.com/christianalfoni/mobx-react-observer

Это позволяет писать export function Component и в девтулзах всё видно без необходимости 2 раза писать имя компонента. Не возникают рандомные редкие баги когда думал что observer есть, а его нет.

Он срабатывает только на ваши компоненты (внешние компоненты в node_modules остаются прежними) + можно исключать папки. Прям колоссальное улучшение DX


я фронтенд-разработчик в компании VK

Зря вы вот это уточнение написали ;)

Автору спасибо за интересный контент. Вопрос: а как вы связываете мобх с серверным состоянием? Используете ли какие-нибудь бинды типа mobx-react-query?
Спасибо!

Привет!

В данный момент мы разрабатываем чисто CSR апку, но на гитхабе у меня можно найти пример работы MobX в режиме SSR (GOZON), а также да, как вы упомянули, можно использовать mobx-tanstack-query + написать некий стор который имеет базовое состояние-слепок стейта

спасибо, интересено, полез смотреть

К Mobx рекомендую пакет, автоматический оборачивающий компоненты в observer:  https://github.com/christianalfoni/mobx-react-observer

Это позволяет писать export function Component и в девтулзах всё видно без необходимости 2 раза писать имя компонента. Не возникают рандомные редкие баги когда думал что observer есть, а его нет.

Он срабатывает только на ваши компоненты (внешние компоненты в node_modules остаются прежними) + можно исключать папки. Прям колоссальное улучшение DX


Концепт автооборачивания действительно улучшает DX, но конкретно этот пакет тянет babel/core и его 15 зависимостей на мегабайты, что на мой взгляд довольно значительная цена. Более легковесных альтернатив с AST трансформацией не видел, но для многих проектов могут подойти трансформации кода регулярками в бандлере - не так надежно, зато 15 строк вместо гигантского дерева зависимостей.

Спасибо за статью. Пользуюсь mobx часто. Но в статье вы не описали пару проблем с которыми я сталкивался. Было бы здорово об этом написать:

1. В примерах вы присваиваете в функциях сразу значения типа store.calls++, но в консоли возникает предупреждение - что менять свойства надо в только в экшенах. Я не до конца понял почему.
2. В асинхронном примере с запросом fetch тоже самое. После await - нельзя писать код который просто стейт меняет, а нужно обернуть в runInAction, или использовать flow из того же mobx, но с ним проблемы с типизацией на Typescript.

Было бы здорово узнать подробнее о такие подводных камнях.

Да, спасибо за внимательность, допишу об этом моменте в статье!

Этот варнинг убирается через настройку

import { configure } from "mobx"

configure({
    enforceActions: "never"
})

Реактивность в Mobx работает синхронно - при изменении реактивных данных сразу срабатывают реакции. Значит, если вы меняете последовательно 2 переменных store.counterOne++ и store.counterTwo++ , то реактовые компоненты перерендерятся после каждой операции. И если оба этих параметра читаются в одном компоненте - будет лишний ререндер.

Да, далеко не всегда это значительно влияет на производительность, но является очевидной точкой оптимизации, чтобы снизить количество ререндеров. Mobx дает для батчей runInAction и по дефолту пишет ворнинг в консоль, если эта оптимизация не используется - это скорее просто good practice, и вполне можно без этого писать приложения, отключая enforceActions. Я же предпочитаю всегда явно батчи делать и экономить ререндеры.

На Хабре было много дискуссий по этой теме, например в этой разбираются централизованные механизмы автобатчинга и их недостатки.

в одном компоненте - будет лишний ререндер.

Насколько я знаю лишнего рендера не будет, потому что 18+ реакт будет автоматически батчить такие апдейты, но mobx реакции вызовутся два раза да

На крупном проекте столкнулись с большими утечками памяти в MobX (в самой последней версии). При детальном разборе оказалось, что компоненты, обёрнутые в observer, при анмаунте просто не очищались GC, они оставались в detached elements (вкладка Memory в DevTools позволяет собрать такие элементы и вызвать GC над ними). Перешли на Valtio – идейно похожий стор, проблемы с памятью просто исчезли.

Привет, все зависит от того как писать !

У нас тоже были утечки памяти, но они были связаны с тем, что в некоторые reaction не были прикинуты сигналы на удаление

reaction(
    fn1,
    fn2,
    {
      signal: this.abortSignal
    }
)

В общем утечек памяти в самом observer я с уверенностью могу сказать что нет, потому что у нас вкладка с фронтом (два фронта: главное приложение + iframe приложение внутри) после работы GC занимает память 84-86 МB

Важный момент: мы не используем React хуки вовсе в слое представления для БЛ (кроме юайных хуков вроде useTable), поэтому прокси не отправляются в массив зависимостей в хуках, как и не используются в замыканиях в хуках. Это может быть связано с утечками

...если интересно, можете посмотреть мои пакеты вроде mobx-view-model...

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

  1. В нем порядок рендера не гарантируется

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

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

Спасибо за комментарий! По пунктам:

  1. Порядок рендера - не проблема. useValue в проде использует useRef-паттерн ленивой инициализации, экземпляр привязан к конкретному хуку, а не к порядку вызовов. С ViewModelStore дополнительно работает пуллинг по id - повторного создания не будет.

  2. Отброшенный рендер - актуально только для сценария без ViewModelStore, что не основной кейс. С ViewModelStore viewModels.attach() просто инкрементит ref-count, что безопасно. Без стора действительно есть ограничение при работе с Suspense - это описано в доке. Перенос mount() в Layout Effect решил бы его, но требует нетривиального рефакторинга - синхронный mount в рендере нужен для SSR и совместимости с mobx-react observer.

  3. StrictMode - работает корректно. В проде useRef сохраняет экземпляр через unmount/remount, lastAttachedInstanceRef предотвращает повторный attach. В дев режиме useMemo может пересоздать экземпляр при remount, но пуллинг по id в сторе не даст дублирования, а без стора очистка отработает корректно. При этом лично я StrictMode не использую - на мой взгляд, инструмент, который меняет поведение в дев и не идентичен поведению в продакшне, применять нельзя.

в комментах нет главного адепта, но есть заменитель

Sign up to leave a comment.

Articles