Effector
https://habr.com/ru/companies/vk/articles/839632/, когда одна из команд VK опубликовала пост с критикой и объяснением, почему они отказываются от этой библиотеки. Я следил за Effector
с 2019 года и всегда считал, что он сложен и делает слишком большой акцент на управлении состоянием.Мой прошлый подход
Мне больше нравился подход с минимальным использованием глобального состояния и применением библиотек типа react-query, SWR, Apollo или Relay, которые берут на себя работу с загрузкой и обновлением данных. В этом случае менеджеры состояния используются только для хранения общих, глобальных данных, которых обычно не так много в большинстве приложений.
Этот подход повлиял на разработку фреймворка Tramvai, над которым я работал. Вот что было внутри:
- Простой менеджер состояния https://tramvai.dev/docs/features/child-app/state-management/ , похожий на Redux, но без глобальных обновлений при каждом изменении.
- React Query для загрузки данных https://tramvai.dev/docs/features/data-fetching/react-query , который полностью отвечал за это, причем данные с API не хранились в менеджере состояния.
- Для SSR использовались глобальные экшены https://tramvai.dev/docs/features/data-fetching/action : для каждой страницы регистрировался набор промисов с действиями, которые нужно было выполнить перед рендерингом страницы.
На этот момент я думал, что это оптимальный подход в React. Но в свое время я не учитывал особенности React, так как React Suspense были чем-то новым на это время и я просто не думал в таких категориях.
Новый взгляд
После перехода в новую компанию я увидел иной подход, основанный на Relay, который активно использует возможности React и при этом значительно упрощает схему, использованную в Tramvai. Relay — это клиент для GraphQL, позволяющий декларативно описывать необходимые данные для компонентов и связывать их между родительскими и дочерними элементами.
Ключевые улучшения:
- Замена глобальных экшенов: Вместо ручного прописывания экшенов используется
useFragment
https://relay.dev/docs/api-reference/use-fragment/ , который описывает набор данных, необходимых для текущего компонента. При подключении компонента родителем нужно связать необходимость загрузки этих данных https://relay.dev/docs/guided-tour/rendering/fragments/#composing-fragments , что повторяется на всех уровнях, формируя полную цепочку необходимых данных для страницы.- Использование React Suspense: Этот подход позволяет улучшить визуальную производительность, загружая только необходимые данные. Появляется возможность пометить данные как необязательные для блокирующей загрузки, используя React.Suspense для отображения скелетонов на месте еще не загруженных компонентов.
Как это можно применить к Tramvai
Применяя эти концепции к Tramvai, можно было бы:
- Позволить каждому компоненту описывать необходимые данные для рендеринга. При этом без использования GraphQL, а как необходимые react-query которые необходимы для отрисовки компоненты, дальше это можно дедублицировать
- Связывать эти данные по цепочке к определению роута, просто лист промисов/query
- При переходе на страницу выполнять список промисов/query, которые нужно выполнить для страницы. При этом ожидая только блокирующие данные
- Использовать React Suspense для компонентов, данные которых еще не загружены. Все это автоматически, на основе информации, был ли выполнен query ранее
Преимущества нового подхода:
- Улучшенная производительность: Пользователь видит плавный переход между страницами, где новая страница уже отрендерена с данными для первого экрана, а остальное догружается в фоне.
- Упрощение организации кода: Разработчику не нужно думать о связи с SSR и последующей загрузке. Он просто определяет необходимые компоненту данные, а остальное происходит автоматически.
В итоге, я считаю, что этот способ оптимален для SSR React-приложений, обеспечивая баланс между производительностью и удобством разработки, а так-же открывает дверь к Server components. А так-же убирает большую часть сложности с State manager для большинства сайтов.