Комментарии 33
Что вам мешает добавить квери параметры в квери ключ? Этим вы решите проблему с одинаковыми ключами и разным количеством запрашиваемых элементов.
Квери-параметры в смысле и размер страницы в квери-ключ положить? Так тоже можно, да. Получатся два разных ключа `{ page_size: 10 }` и `{ page_size: 20 }`. В целом на проекте так и делаем. Про строки `-fixed-size`, `-paginated` я для примера явного разделения разных ключей привёл пример. А так да, и параметры туда можно развернуть, спасибо за замечание
Работаю с Vue и Pinia, поэтому могу не знать особенностей работы MobX, но нельзя разве сделать так, чтоб при уходе со страницы стор не очищался? Беглый гугл выдает пакет mobx-persist-store.
Хотелось бы побольше узнать, почему было легче переписать все на другую технологию
Сделать так, чтоб при уходе со страницы стор не очищался
Да, конечно, можно. Как уже говорил, всё, что есть в React Query, можно сделать вместе с MobX. По идее в том числе предложенным вами пакетом, просто вместо localStorage подсунуть какой-то глобальный storage (хранение в localStorage нам было бы избыточно). В целом можно бы было ещё проще — сделать локальный стор подобием глобального и сделать свои механизмы работы с таким подобием кеша
Но снова: пришлось бы явно разделять, как стор ведёт себя на клиенте и на сервере (на клиенте сохраняем состояние, на сервере всегда создаём новое); пришлось бы задумываться, как обрабатывать состояния загрузки при наличии persistent-состония; как реализовать stale-while-revalidate, а не просто сбрасывать состояние и загружать новое; как работать с пагинируемыми списками и так далее — всё это уже реализовано в React Query
Здесь же немаловажен вопрос поддерживаемости. Поскольку самая частая цель сторов на проекте была в том, чтобы "загрузить данные из API и хранить", переписывание на другую технологию заключалось просто в том, чтобы удалить стор, написать функцию запроса и обернуть её в useQuery/useInfiniteQuery, подобрав query-ключи — это буквально всё. Остальную рутину решает React Query под капотом, в том числе гидрацию при с SSR. На те грабли, которые я привёл в статье, мы бы в любом случае наступили, реализовывали бы мы своё решение или использовали бы что-то новое. Поэтому да, скорее, легче было просто переписать
А как с Vue+Pinia? Там это проще решается? Я сам никогда Vue плотно не изучал, интересно, как там
Vue во всем гораздо проще, так и библиотечки под него. Но конкретно в этом случае, будет так же плагин под Pinia. Что бы был persist
С реакт квери доходит до смешного, что они дропнули поддержку старых safari например. Дропать старое ок, но часто это какие-то проценты пользователей. А что может быть такого, в этой библиотеке, что она перестает поддерживать старые браузеры.
Вместо ухода от mobx можно было просто написать адаптер под react query и использовать его в рамках существующих классов. Тем более его уже написали и достаточно скачать библиотеку react query mobx. Касаемо mobx, он в первую очередь нужен для реактивности, а ваша проблема заключалась не в том, что mobx какой-то не такой, а в том, что использовался неправильный инструмент, вам нужен был клиентский кэш, вот и все.
Длинные цепочки наследования - зло, нужно предпочитать композицию
Да, безусловно, проблема не в том, что MobX какой-то не такой, и я указал в статье, что просто дело в том, что React Query больше подошёл под наши потребности, а для своих нужд MobX — отличный инструмент
Мне кажется, даже если бы мы написали адаптер — это был бы больше костыль и лишнее усложнение в поддерживаемости кода. Мы бы в и без того сложную структуру встроили кеширование через React Query, а вместе с тем потянулись бы и сложности в SSR и другие. Вместо этого легче было написать запрос и завернуть его в useQuery/useInfiniteQuery, а все состояния загрузок, гидрацию, ревалидацию React Query решет под капотом. Для связи MobX и React Query нам достаточно было реагирования useEffect'ами без дополнительных обёрток. Вероятно, для проектов, где реактивность нужна более тонкая, адаптер бы подошёл лучше
По поводу пакета react-query-mobx, прошу прощения, мб не так искал — не нашёл. Нашёл только mobx-ecosystem/mobx-react-query с пятью скачиваниями за последнюю неделю. Кажется, это не то, что вы имели в виду
Про наследование да, нужно не заиграться в ООП во фронтенде хехе....
Если у вас половина кода это ручной фетчинг данных через MobX, то можно было вынести это в библиотеку с кэшем и перестать перезапрашивать одно и то же. К тому же есть и библиотеки вроде mobx-persist-store, где можно в пару строчек сделать синхронизацию observable с sessionStorage / localStorage / IndexedDB. И наследование не обязательно:
class DashboardStore {
products = new RequestStore(api.users.get)
products = new RequestStore(api.products.get)
orders = new RequestStore(api.orders.get)
}Один такой RequestStore тоже заменил бы вам половину проекта. Даже классы не обязательны:
const counter = makeAutoObservable({
value: 0,
increment() {
counter.value++
}
})Просто классы удобнее с TypeScript, так как не нужно описывать тип стора вручную. От этой проблемы страдает например Zustand: https://zustand.docs.pmnd.rs/guides/typescript
Спасибо за идею, действительно интересно бы было сделать такую либу. Но, боюсь, нам бы это дало не меньше головной боли с ревалидицией кеша (в смысле механизм stale-while-revalidate), его сбросом, его редактированием, отключением синхронизации на сервере и включением на клиенте. Всё так же надо было реализовывать вручную гидрацию стора на клиенте по данным с сервера. В общем, всё это действительно добавляет лишнего оверхеда, когда в React Query это уже всё реализовано и поддержано сообществом
Как человек, написавший несколько проектов на react-query за последние полгода, опишу реальные проблемы это библиотеки. Первое это невозможность иметь несколько представлений для одних и тех же данных. Например мы отрисовали 2 списка в разных компонентах через хук и теперь нужно отсортировать список. Чтобы сортировка одного списка стриггерила сортировку другого нужно либо обновлять данные глобально:
queryClient.setQueryData(['projects'], newOrder);и тогда мы имеем лишь одно представление для данных, что на больших проектах будет ограничивать
либо используем функцию сортировки в двух компонентах, но как-то костылями триггерим перерисовку второго. В Mobx же всё просто - сущности это observable, а разные представления сущностей - это computed. И computed'ов можем быть сколько угодно:
class ProjectStore {
projects = []
sortOrder = 'default'
get sortedProjects() {
return this.sortOrder === 'alphabetical'
? this.projects.slice().sort((a, b) => a.name.localeCompare(b.name))
: this.projects
}
Второе что react-query это всё-таки просто кеш. Он не даёт вообще никаких удобств для написания интерактивных приложений. Чтобы жить только на react-query приходится использовать React Context с хуками, а это перерисовка лишних компонентов на каждый чих + невозможность использовать эту бизнес-логику вне реакт компонентов. По моему опыту чем больше в проекте интерактива, тем сильнее код превращается в лапшу из хуков дёргающих друг друга. Поэтому для меня react-query - классно для неинтерактивных приложений где больше чтения чем записи, либо запись примитивная. А для сложной клиентской логики нужен стейт-менеджер.
Спасибо, что поделились опытом и подводными камнями!
Невозможность иметь несколько представлений для одних и тех же данных. Например мы отрисовали 2 списка в разных компонентах через хук и теперь нужно отсортировать список
У нас проект, честно сказать, не прям очень интерактивный и всё, что сложнее сортировки массива, мы просто перемещаем на сторону API. В тому же, скорее всего, такой список у нас был бы пагинируемым, а на клиенте пагинируемый список не отсортируешь, поэтому точно эти манипуляции мы бы оставили бэкенду
Ну и, вероятно, у вас чуть другой кейс. Для нас кейс с разными представлениями данных выглядел, скорее, так: юзер зашёл на главную — увидел там какой-то набор сущностей с минимальным количеством данных; зашёл в деталку какой-то сущности — там расширенный набор данных (данные из списка переиспользовать не получится); зашёл в поиск — там тоже некоторый список сущностей с разным количеством данных и не факт, что этот список пересекается с другими. В общем, для нас вопрос представления одних и тех же данных разным образом не стоял остро, но я согласен с вами, с React Query это, действительно, может быть неудобно
react-query это всё-таки просто кеш. Он не даёт вообще никаких удобств для написания интерактивных приложений
Полностью согласен с вами (и об этом упомянул в статье). В нашем случае не нужно было много интерактивности, поэтому React Query отлично подошёл. Максимум интерактивности в нашем кейсе — это добавление каких-то сущностей в избранное, например. Точно не дашборды или игровые механики
Вместо итога "мы получили значительный прирост воспринимаемой скорости работы приложения" было бы интереснее взглянуть на метрики перфоманса после перехода на react query. Воспринимаемая скорость, все же, штука субъективная, а вот метрики точно скажут, где стало лучше/хуже и насколько.
На самом деле, я пока писал статью, жёстко загорелся идеей написать сколько-то объёмные два приложения с одинаковым функционалом очень похожим на наш кейс, но чтобы общение с API было бы реализовано по-разному: через MobX и утилитарные классы и React Query. Это я хотел размеры бандлов сравнить, но чтобы не просто размер библиотек сравнивать, а чтобы tree shaking какой-нибудь сработал и другие оптимизации. И сделал это с помощью Codex от OpenAI за пару вечеров. Выглядело ужасно, но для эксперимента было достаточно. В итоге я не увидел значительного различия совсем. Почти одинаковые цифры по размерам бандлов. А что касается нашего кейса, поскольку две библиотеки пока живут вместе в одном проекте, не получится сказать о каком-то улучшении в этом плане
Если говорить о перерендерах, то, как уже отметил в статье, MobX позволяет точечно реагировать компонентам на изменение конкретных полей, которые они слушают. В React Query всё-таки когда изменяем кеш вручную, это делается иммутабельно, поэтому там могут быть лишние ререндеры. Но здесь уже вопрос о том, приемлемо ли это для конкретного приложения. Для нас это было ок
Касательно перформанса в смысле "абсолютной" скорости, а не "воспринимаемой", в плане общения с API фронтовая библиотека вряд ли что-то может предложить для улучшения ситуации с сетью, поэтому мне показалось более резонным сделать упор на воспринимаемую скорость и избавление от лишних запросов. Так, для среднего сеанса пользователя (типа "зашли на главную, подгрузили список, оттуда — в деталку, потом обратно"), я бы сказал, что мы избавились от лишних перезапросов почти полностью, поскольку данные не обновляются очень часто. В случае нашего предыдущего сетапа без кеширования у нас бы на каждый шаг происходил новый запрос
Спасибо за комментарий. Надеюсь, я донёс, почему рассматривал кейс по большей мере с точки зрения воспринимаемой скорости
Спасибо за статью, но сравнивать Mobx, который просто добавляет реактивность объектам (можно его воспринимать как полифилл для deprecated метода Object.observe), и библиотеку для запросов не слишком корректно. На мой взгляд, в статье нужно было сравнивать "подход на классах, в котором самостоятельно строится работа с API" и "готовая библиотека, завязанная на Реакт и работающая на хуках". Зачем здесь упомянут Mobx, который просто вынесенные в классы данные делает реактивными, из статьи неясно. Можно просто убрать makeAutoObservable и сделать ручной forceUpdate компонентов либо увеличивать counter, и весь код и проблемы останутся такими же, только без упоминания Mobx в статье.
Все описанные недостатки относятся к архитектуре и реализации в вашем проекте (повторные запросы, громоздкие сторы с длинной цепочкой наследования, ручная обработка состояний загрузки, отсутствие строгих правил реализации) и говорят только о том, что команде такой подход не подошел - не получилось грамотно выделить хранилища и логику и отвязать от UI. А вот подход с хуками и привязкой к рендеру команде более подошел. Соответственно, вывод должен был быть "в нашей команде не получилось построить эффективную архитектуру работы с API, поэтому взяли готовую библиотеку".
Но это вовсе не значит, что у других команд тоже не получится без сторонних библиотек покрыть все эти кейсы, и в ряде случаев это будет эффективнее и удобнее, чем React Query. Например, последние 6-7 лет я использую подход, который базово описал здесь.
ручки описываются 1 раз
валидаторы запроса-ответа генерируются автоматически из TS-моделей
уникальные идентификаторы уже есть (название файла, а при динамических параметрах можно считать hash от request body)
каждая функция как объект обладает реактивным состоянием
getUser.state.isExecuting,getUser.state.errorнесложно сделать кеш, сравнивая
getUser.state.validTill - Date.now(), то есть функция будет просто не вызывать запрос на бэк, а данные будут браться изstore.userпока не истечет это время. И много других интересных преимуществ, подробнее описал там
Но кеширование на фронтенде всегда было плохой идеей, так как только бэк знает, когда обновились данные. И это должно настраиваться либо http-заголовками (чтобы браузер сразу брал из кеша), либо передачей version в запросе-ответе, если цель - оптимизировать запросы в БД (бэк, увидев совпадение version, отдаст данные из кеша, чем кардинально оптимизирует нагрузку). Либо если очень нужен фронтовый кеш - то его можно неинвазивно прикрутить практически к любому проекту через Service Worker self.addEventListener("fetch") и не менять ни одной строчки кода проекта, а бонусом - еще и оффлайн-режим и удобный неинвазивный механизм моков.
И только в самом крайнем случае стоит вшивать кеши в код в императивном порядке (хотя примера придумать не могу). К слову, если данные класть в глобальный стор, а не в постраничный (который разрушается при переходе на другую страницу), то в классовом подходе кеш уже получится автоматически
useEffect(() => { api.getUser() }, [])
return store.data
? <div className={api.getUser.state.isLoading && styles.isLoading}>{data}</div>
: null;так в фоне запрос вызовется, но компонент будет отрисован сразу из "кеша" - данных в сторе, поэтому ни скролл не сбросится, ни визуальная скорость приложения не пострадает. А сам запрос можно оптимизировать рецептами, которые выше привел.
Еще момент про SSR - его тоже несложно сделать без готовых библиотек. Как известно, useEffect на сервере не вызывается, но вот useState(() => api.getUser()) вызовется. И если как выше писал api выделить в отдельный слой, то можно сделать
// вызов всех апи из любой вложенности компонентов
renderToString(App);
// псевдокод, реальный можно в репо посмотреть
await api.every(fn => !fn.state.executing);
api.every(mock);
res.send(renderToString(App).replace('SERVER_DATA', JSON.stringify(globalStore)));Вот и весь SSR, поддерживающий любую вложенность - то есть не как в Next, где запросы можно вызывать только на страницах, а можно в 20 глубине вложенности (например в каком-то селекте) вызвать апи, и данные сохранятся в стор и сериализуются для клиента. При этом независимый слой апи полностью отвязан от UI и можно без проблем использовать в любой рендерилке. Если нужен CSR-only, можно по урлу отдавать с бэка пустой html (то есть в роутинге сделать 1 настройку - ssr: true | false для конкретного роута).
В общем, если не хочется использовать готовые библиотеки типа React Query, то вполне можно придумать множество интересных и эффективных подходов, при этом MobX как полифилл реактивности очень помогает "дружить" независимые слои (сторы, апи, UI разных фреймворков). А чтобы в проекте были "строгие правила реализации" нужно не ставить 100500 сторонних библиотек с тоннами документации (это структурирует только микрочасть проекта), а должен быть грамотный техлид или архитектор, который будет синхронизировать команду.
Изначально наткнулся на статью из-за слова MobX, но про него тут ничего нет, поэтому в целом описал альтернативные подходы для решения проблем, описанных в статье)
Спасибо за очень развернутый комментарий и примеры! Мне тоже нравится MobX с классовым подходом тем, что можно чётко отделять логику и отображение
Вы правильно заметили, что подход с MobX включал в себя самостоятельное выстраивание флоу по работе с API и этот подход нам не подошёл. Мне хотелось подчеркнуть, что React Query решает множество проблем, которые мы пытались решить сами и что использование готовой библиотеки может помочь избежать риска совершить ещё больше ошибок. Да, всё можно выстроить грамотно и к способности это делать, безусловно, нужно стремиться. Просто в нашем случае было дешевле сменить подход
Я бы тоже хотел подчеркнуть, что "подход с MobX" - это не "подход с MobX", от него только makeAutoObservable как замена forceRender компонентов при изменении данных. 99.99% - это ваш код в ООП стиле и ваша архитектура, на которую Mobx не влияет. На нем можно писать очень по-разному - и без классов, и даже без plain объектов (как я показал на примере функций). Поэтому использование слова Mobx в статье - это только кликбейт, потому что никакой специфики, кроме как автоматических ререндеров UI, он не приносит в контексте статьи, а статья при этом и не про UI, а про совсем "далекую степь" - работу с API.
Отчего у читателя складывается неверное впечатление, будто Mobx хуже React Query в каких-то кейсах, но на самом деле совсем не так - это непересекающиеся вещи, которые нельзя сравнивать, и у которых совершенно разное назначение. То, что можно хранить данные в отдельном объекте и обернуть его в observable и то, что можно хранить данные внутри React Query и возвращать их через хук - это хоть и звучит похоже (объект там и там), но вещи совсем разные. Да, если пытаться использовать стейт вне рендера (mobx ни при чем, просто класс или объект) и стейт возвращаемый React Query внутри функции рендера, то будет дубляж данных, в этом плане их действительно можно противопоставить. Но противопоставление именно отдельного стейта и внутрикомпонентного стейта, с реактивностью это никак не связано.
Хотя React Query тоже кеш хранит глобально, и получается тройной конфликт (глобальный стор скрытый внутри React Query, его выдача в хуке внутри рендера, попытка вынести в независимый слой вне Реакта). Тут действительно можно сказать - либо React Query либо независимый слой хранилищ, потому что все вместе - большая каша. Но опять же, это не про реактивность)
В Ангуляре это идёт из коробки))
Тут нужен $mol
Вот вы пишите, что у вас большой проект и сравниваете огурцы с помидорами. Какой смысл об этом сообщать, если у вас нет понимания, что стор - это слой представления, и в нём никак не должны решаться задачи кеширования и ретрая. И логика в сторе мобх должна, грубо говоря, дисейблить кнопочку на основании данных полученных из слоя сервисов. А если вы так лихо меняете мобх на реакт квери, то ни о какой сложной логике и речи быть не может.
Для лучшего понимания могу привести пример: вот есть sdk яндекс карт, и вы дергаете методы этого сдк, и оно как то делает запросы и вас это не волнует, вы просто слушаете события и выводите данные этого сдк, не заморачиваясь над кешированием и обработкой сетевых ошибок. Именно такое сдк и должно быть у вашей системы, а реакт разрабы просто подсасывают данные и через мобх весело рендерят реактивные списки и прочие компоненты.
Ничего удивительного, слабые и некомпетентные разработчики не в состоянии написать 1 раз класс/функцию для работы с АПИ как удобно именно именно им, если нужен кэш, то с кэшем, если ретрай. то с ретраем и т.д.
Для этого они просто берут корявые готовые решения, а свою некомпетентность маскируют классикой которой уже десятилетия - они просто говорят, не хочу изобретать велосипед. Но на сама деле не хочу у них означает - не могу я это реализовать.
Ну т.е. просто взять и отказаться от MobX просто так, ну тут как бы ничего кроме сочувствия даже на ум не приходит.
Мне кажется, что ни один разработчик, сколько бы он ни был компентым, не сможет написать класс/функцию один раз и пользоваться дальше на протяжении всей жизни проекта. В начале не нужен кеш — потом стал нужен. Сначала не нужны ретраи — потом стали нужны. В нашем случае проект развивается уже больше полутора лет
У нас было два пути: 1) Продолжать реализовывать свои решения, теряя ресурсы (деньги, время) на отладку и доведение до идеала (не сомневаюсь, спустя N итераций мы бы всё же дошли до идеала); 2) Использовать готовое, проверенное сообществом решение
"Взять и отказаться от MobX просто так" не произошло так легко. Безусловно, рассматривались различные варианты. И в нашем случае лучше сыграл тот, который описал в статье. Это совершенно не значит, что MobX плох, или ранее избранный путь развития был плох сам по себе. Это значит, что для наших конкретных нужд старый подход себя изжил
Мне кажется, что ни один разработчик, сколько бы он ни был компентым, не сможет написать класс/функцию один раз и пользоваться дальше на протяжении всей жизни проекта
Её можно написать один раз на проекте X в каком ни будь 2018 году и пользоваться ей на проектах Y,Z и т.д. по сей день.
В начале не нужен кеш — потом стал нужен. Сначала не нужны ретраи — потом стали нужны.
И что? Это проблема? Просто реализуйте этот функционал, это элементарно. Это же операционную систему написать.
В нашем случае проект развивается уже больше полутора лет
И что? Потратить суммарно 8 часов на для реализации класса обертки для API запросов с ретраями и кэшем это проблема?
1) Продолжать реализовывать свои решения, теряя ресурсы (деньги, время) на отладку и доведение до идеала (не сомневаюсь, спустя N итераций мы бы всё же дошли до идеала);
Понятно, классические отговорки и отмазки. Вы что, считаете react-query идеалом? :D Ну я думаю очевидно что нет. А значит аргумент сразу теряет силу. Ибо если бы реально был идеал, то и да, я полностью соглашусь что смысла делать свою реализацию нет. Но как бы идеала нет из готовых решений. Так что...
2) Использовать готовое, проверенное сообществом решение
Типичная не компитентность. Это всё равно, что складывать 5 + 5 на калькуляторе говорить что ты умеешь считать. Перед вами не задача 3х тел, где нужно учесть всё, и гравитацию и магнитное поле и т.д и т.п. А реально перед вами задача 5 + 5, а вы даже её не в состоянии решить. Зато называете себя разработчиками.
Это значит, что для наших конкретных нужд старый подход себя изжил
Ну если вы не состоянии решить 5 + 5 без сторонней библиотеки, и на этом фоне вы отказываетесь от MobX, то да, вашему проекту уже без разницы, можно максимально сильно его втаптывать в землю. Ибо он всё равно обречен быть похоронен с таким подходом.
Почему вам кажется, что стор должен ограничиваться только контролированием состояния UI? При желании очень хорошо можно выстроить взаимодействие с API и через MobX-сторы: глобальный стор для контроля за всеми запросами, общей конфигурацией; свой observable-экземпляр под каждый запрос со своим внутренним состоянием выполнения, однообразная отмена запросов и так далее. Опять же — вопрос в том, какой ценой команда готова это реализовывать и сколько шишек набьёт по пути
Статья сравнивает подходы. В одном подходе использовалась плотная связка стейт-менеджера и походов в API, в другом — фактически отказ от стейт-менеджера. Второй подход оказался нам ближе. Если бы были опущены конкретные библиотеки и детали, статья бы не несла пользы. Тогда бы было сравнение абстрактного подхода с другим абстрактным подходом. А так читатель хотя бы может увидеть конкретные примеры
Почему вам кажется, что стор должен ограничиваться только контролированием состояния UI
Потому что уже 30 лет над этой проблемой бьются и придумали многослойную архитектуру? Плюс вы на собственном опыте ощутили все прелести смешения слоев приложения.
Ну и ещё задайте себе вопрос, почему вы выбрали, в своё время, хайповый мобх, а теперь идете по тем же граблям и выбираете хайповый реакт квери (у которого кстати 30 параметров в функции, ага). Может быть что-то делаете не так? А может быть ваше приложение на самом деле не слишком то и большое и вам реально не нужен глобальный стейт интерфейса. Большим приложением обычно олды называют "толстый клиент" на котором много бизнес логики и эта логика не должна быть завязана на инфраструктуре ui библиотеки/фреймворка.
Я кстати вас не отговариваю от вашего решения, на самом деле оно действительно вам подходит, потому что у вас приложение по сути просто делает запрос и рендерит ответ. Усложнять слоями действительно смысла нет. Ну и может быть вам даже проще взять какой нибудь useAsync из готовой библиотеки, чтобы не тянуть тяжелый реакт квери, думаю вам вполне и этого хватит.
Да, я понял ваш довод про слои архитектуры. Я больше о том, что не обязательно одному и тому же стору знать о деталях взаимодействия с API (там же и кеширование, состояние пагинации и другие) и одновременно контролировать UI. У нас для взаимодействия с API были утилитарные сторы, которые брали на себя рутину (а-ля ApiStore, ApiRequestModel, PaginationModel). А сторы страниц использовали утилитарные сторы и были сосредоточены на отображении. Другое дело, что, действительно, все эти конструкции были избыточны в нашем случае
Повторные запросы: локальные сторы обнуляются при размонтировании, поэтому при возврате на страницу данные перезапрашиваются.
Так вроде это ожидаемо - страничка должна показывать максимально акутальные данные в момент рендера.
Если такого требования нет - значит стор необходимо перенести из локального чуть выше, в глобальный (ака глобальная переменная).
Условно - дёрнуть useEffect -> loadUser в корне приложения один раз. И потом дёргать ещё раз только после update/delete юзера.
Да, раньше было ок то, что данные обнулялись. Просто в какой-то момент главная стала жирной, да и объективно её незачем было запрашивать при каждом перезаходе
В целом всё так, как вы предлагаете: можно было превратить в глобальный стор. Просто это же всю обвязку самим придётся реализовывать: у нас там фильтрация сущностей — их надо учитывать, stale-while-revalidate, политики инвалидации (staleTime, cacheTime, ручная инвалидация по ключам с полным или частичным совпадением). Пагинация даёт больше сложностей, тот же SSR
Это всё можно сделать и без React Query, конечно. В частном порядке для одной лишь какой-то страницы — тем более. Но при масштабировании это всё превратится в снежный ком, который заставит совершить много ошибок и исправить их. Вместо этого мы предпочли сделать проще и так, чтобы лучше подходило под наши нужды
Так и не понял зачем нужно было уходить от MobX, если можно было заюзать одну из опенсорс либ интеграции MobX с tanstack query (например mobx-tanstack-query).
В итоге уйдя от МобХ вы будете прибивать бизнес логику к механизмам рендеринга , тем самым подшивать бизнес логику в слой представления.
Интеграция двух инструментов только усложнила бы финальное решение, просто потому что появилось бы больше мест для потенциальной ошибки при поддержке этого всего. В то же время оказалось, что в нашем случае чаще всего нужно просто загрузить данные и отобразить их. То есть гибкость MobX оказывалась избыточной. Поэтому меньшим из зол в нашем случае оказалось перейти на React Query. Пусть и появляется бОльшая связь с представлением, флоу запросов получается более очевидным и не требующим такой степени абстракции. Если действительно понадобится сильнее разделить запрос (и возможную обработку) данных и отображение, будем думать, спасибо за идею. Хотя пока в большинстве случаев в нашем кейсе это всё-таки оказывалось лишним по крайней мере для наших целей
Включите, пожалуйста, подсветку синтаксиса в примерах кода в вашей статье.
Удалить полпроекта: как мы переписывали MobX‑сторы на React Query в большом Next.js‑проекте