Проведите день в спа-салоне, работая над своим SPA с помощью удаленных данных

📑 TLDR:

  1. Используйте структуру данных RemoteData из библиотеки @ngspot/remote-data для описания данных, запрашиваемых из API.
  2. Для достижения наилучших результатов используйте RxJS и пользовательский оператор trackRemoteData из библиотеки @ngspot/remote-data-rx.

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

🤔 Пример наивного подхода

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

Хотя в приведенном выше коде есть много проблем (например, ручная подписка, неиспользование обнаружения изменений OnPush, неиспользование trackBy для цикла ngFor, ошибка из-за потенциального условия гонки и т.д.), приведенный выше код работает. Пожалуйста, пока не обращайте внимания на недостатки. Большинство из них будут устранены к концу статьи, а остальные опущены для простоты.

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

Достаточно просто! Но подождите, что если API вернет ошибку? Я хочу отобразить что-то пользователю в этом случае. Я знаю, как с этим справиться! Поэтому я ввел еще одно свойство: error!

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

В приведенном примере данные загружаются автоматически при инициализации компонента, но все может быть иначе. Что если вы хотите вывести на экран подсказку с инструкциями для случая, когда данные еще не были запрошены? Это очень много спагетти-кода!

💡 RemoteData в помощь!

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

Можно добиться лучшей безопасности типов, если создать специальный тип для каждого из состояний, а затем использовать функцию объединения TypeScript.

Теперь я создам несколько функций-конструкторов, которые будут возвращать RemoteData для каждого возможного состояния запроса (1) не запрошен, (2) загрузка, (3) успех и (4) ошибка).

Когда все это готово, вот переписанный компонент:

Это намного чище! Нужно поддерживать только одно свойство, и это свойство подходит для всех случаев использования. Библиотека @ngspot/remote-data, по сути, была переделана. Не стесняйтесь использовать ее!

Но я могу сделать лучше! Читать дальше.

💪 Использование возможностей RxJS

Помните многочисленные проблемы, упомянутые в начале статьи?

Среди них есть баг, связанный с условием гонки. Если пользователь много раз быстро нажмет на кнопку «Загрузить товары», то будет запущено множество запросов. Есть вероятность, что из-за временных рамок сети ответы на эти запросы будут возвращаться не по порядку. Ответ на запрос, связанный с самым первым щелчком, может вернуться последним. Это означает, что пользовательский интерфейс может отображать не самые свежие данные.

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

Без лишних слов, вот переписанный компонент, использующий реактивные потоки и структуру данных RemoteData.

Это решение гораздо более надежно. Здесь нет ручных подписок. Данные передаются в шаблон через реактивные потоки с помощью трубы theasync, что позволяет использовать обнаружение изменений OnPush. Наконец, условия гонки обрабатываются с помощью оператора switchMap, который автоматически отменяет все предыдущие запросы в полете и запускает новый.

RxJS позволяет создать пользовательский оператор, используя несколько существующих операторов. Именно это я и сделал в приведенном выше примере — я взял операторы, используемые для обработки загрузки RemoteData, успеха и ошибок, и извлек эти операторы в пользовательский оператор под названием trackRemoteData. Найти оператор trackRemoteData можно в библиотеке @ngspot/remote-data-rx. В него встроена еще пара колокольчиков и свистков.

С ними код становится еще проще.

🧡 Кредит там, где положено кредит

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

  • С чего все началось: «Как Elm уничтожает антипаттерн пользовательского интерфейса».
  • https://www.npmjs.com/package/ngx-remotedata
  • https://github.com/daiscog/ngx-http-request-state

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

Желаю вам счастливого программирования!

👏 Особая благодарность Ане Бока за рецензию на эту статью.

Оцените статью
devanswers.ru
Добавить комментарий