Оценить качество материала и подачу материала автором видео:
Front-end
Трудоустройтесь middle front-end разработчиком на React JS (TypeScript) за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио
Трудоустройтесь middle back-end разработчиком за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио
Получите коммерческий опыт на реальных стартапах, прокачайте tech & soft навыки, научитесь работать в команде, проходить собеседования и получите первую работу в IT!
Автоматически сгенерированные RTK Query хуки предоставляют производные булевые флаги
Query Loading State,
которые отражают текущее состояние данного запроса:
isLoading будет true, когда запрос выполняется впервые для данного хука и данные ещё недоступны.
Используется для отображения Skeleton или пустой заглушки, пока данные загружаются впервые.
isFetching будет true, когда запрос выполняется для заданного эндпоинта и параметров запроса,
но не обязательно в первый раз. Используется для показа текущих данных с добавлением визуального индикатора
обновления данных (LinearProgress).
Выведем в консоль isLoading, isFetching в компоненте PlaylistsPage.tsx
При первой подгрузке будем использовать свойство isLoading и показывать пользователю skeleton.
🔗
Сейчас скелетон мы не будем реализовывать, т.к. на верстку сейчас мы акцент не делаем. Но в качестве домашнего
задания можете реализовать самостоятельно используя библиотеку react loading
skeleton
А при пагинации свойство isFetching и отображать LinearProgress
Результат: разобрались с отображением загрузки для PlaylistsPage 🚀
Глобальный loader
Мы реализовали отображение загрузки только для получения плейлистов. Но, нам же нужно показывть индикатор загрузки
при добавлении / удалении/ редактировании плейлиста. А в дальнейшем все CRUD операции для других страниц.
Соответственно хотелось бы иметь какой-то глобальный индикатор загрузки.
Существует несколько вариантов, чтобы реализовать такой loader:
написать кастомный middleware
создать slice и в нем использовать builder.addMatcher
написать кастомный хук useGlobalLoading
Остановимся на самом простом, но рабочем варианте. Напишем кастомный хук useGlobalLoading
RTK Query возвращает объект результата, который содержит значения error и isError,
которые можно использовать в компоненте для отображения и обработки ошибок:
Но этот вариант не сработает из-за TS. Согласно документации в RTK Query есть несколько типов ошибки.
FetchBaseQueryError
Когда ошибка корректно возвращается из базового запроса (base query), RTK Query предоставит эту ошибку напрямую
'FETCH_ERROR' - ошибка сети
'PARSING_ERROR' - ошибка во время парсинга
'CUSTOM_ERROR' - пользовательский тип ошибки
'HTTP status code' - status code ошибка (401, 403, 500 и т.д.)
SerializedError
Если пользовательский код выбросит непредвиденную ошибку (например, throw new Error).
В таком случае ошибка будет преобразована в формат SerializedError.
Приведение типов через as (type assertion) может привести к непредвиденным ошибкам, поэтому использовать можно
только если есть уверенность в типе данных:
Создайте в src/common/utils/isErrorWithMessage.ts утилитную функцию isErrorWithMessage
src/common/utils/isErrorWithMessage.ts
/* Синтаксис error is { message: string } означает, что если функция возвращает true, TypeScript будет рассматриватьerror как объект с обязательным строковым свойством message. */exportfunctionisErrorWithMessage(error:unknown): error is { message: string } {return (typeof error ==='object'&&// Проверяем, что error – это объект error !=null&&// Убеждаемся, что это не null'message'in error &&// Проверяем, что у объекта есть свойство 'message'typeof (error asRecord<string,unknown>).message ==='string'// Убеждаемся, что это строка )}
Функция isErrorWithMessage - типовой предикат (type predicate) в TypeScript. Она проверяет, является
ли переданное значение error объектом, содержащим свойство message типа string.
error is { message: string } – типовой предикат, который говорит TypeScript: "если
функция возвращает true, тогда error считается объектом с полем message типа string".
Без этой записи, TS будет видеть только boolean.
Утечка чувствительной информации
Серверные ошибки часто содержат детали внутренней реализации: stack trace, имена таблиц, SQL-запросы,
внутренние пути, названия файлов и т.д. Такая информация может быть использована злоумышленниками для атаки на систему.
Подсказки для хакеров
Сообщения типа Database connection failed или Invalid credentials for user admin подсказывают злоумышленникам,
на каком этапе возникла проблема, и могут ускорить подбор уязвимости.
Раскрытие архитектуры приложения
Сообщения вида NullReferenceException in UserService или ModuleNotFoundError in utils/logger.js раскрывают
детали архитектуры, что упрощает анализ системы для атакующего.
Непредсказуемость содержимого ошибок
Сервер может вернуть всё что угодно: приватные ключи, токены, пароли и другие критичные данные, если они случайно
попадут в текст ошибки.
Как правильно
Показывать пользователю только безопасное универсальное сообщение, например:
Что-то пошло не так на сервере. Пожалуйста, попробуйте позже.
или
Server error occurred. Please try again later.
Все подробности ошибок должны логироваться на сервере и быть доступны только разработчикам.
Optimistic Update (оптимистичное обновление) - подход в разработке приложений, при котором
пользовательский интерфейс (UI) обновляется до того, как сервер подтвердит успешность операции.
Другими словами, приложение предполагает, что операция (например, отправка данных на сервер)
завершится успешно, и сразу вносит изменения в UI. Если операция на сервере завершится
неудачно, изменения в UI откатываются:
Преимущества Optimistic Update:
улучшение пользовательского опыта: пользователь видит мгновенный отклик на свои действия, что делает приложение
более отзывчивым;
снижение ощущения задержки: даже при медленной обработке запроса сервером, пользователь сразу видит
обновленный UI;
Недостатки Optimistic Update:
сложность реализации: нужно учитывать возможность отката изменений, если запрос завершится ошибкой;
потенциальная несогласованность: если сервер вернет ошибку, а приложение не сможет корректно откатить изменения,
UI может остаться в некорректном состоянии;
Редактирование плейлиста
Перепишем на optimistic update редактирование плейлиста.
🔗
onQueryStarted — это часть конфигурации в RTK Query, которая позволяет вам выполнять побочные эффекты и обрабатывать запросы до того, как они будут отправлены. Эта
функция особенно полезна для оптимистичного обновления состояния или для выполнения других асинхронных операций,
которые могут понадобиться до завершения запроса.
🔗
updateQueryData локально
обновляет данные в кэше для конкретного запроса и возвращает объект действия. При dispatch этого действия,
возвращаемое значение от dispatch - объект patchResult. Если вызвать patchResult.undo(), запустится отмена
изменений
🔗
queryFulfilled — это promise, возвращаемый RTK Query, который разрешается, когда запрос
успешно завершен. Вы можете использовать queryFulfilled, чтобы выполнить дополнительные действия
после успешного завершения запроса или для обработки ошибок, если запрос не удался.
playlistsApi.ts
playlistsApi.ts
exportconstplaylistsApi=baseApi.injectEndpoints({endpoints: build => ({/*...*/ updatePlaylist:build.mutation<void, { playlistId:string; body:UpdatePlaylistArgs }>({query: ({ playlistId, body }) => ({ url:`playlists/${playlistId}`, method:'put', body }),asynconQueryStarted({ playlistId, body }, { dispatch, queryFulfilled }) {constpatchResult=dispatch(playlistsApi.util.updateQueryData(// название эндпоинта, в котором нужно обновить кэш'fetchPlaylists',// аргументы для эндпоинта { pageNumber:1, pageSize:2, search:'' },// `updateRecipe` - коллбэк для обновления закэшированного стейта мутабельным образом state => {constindex=state.data.findIndex(playlist =>playlist.id === playlistId)if (index !==-1) {state.data[index].attributes = { ...state.data[index].attributes,...body } } } ) )try {await queryFulfilled } catch {patchResult.undo() } }, invalidatesTags: ['Playlist'], }), }),})
при изменении плейлиста он сразу обновляется на UI 🚀
если запрос завершится ошибкой, тогда изменения в UI откатятся к предыдущему состоянию 🚀
selectCachedArgsForQuery
Если перейти на другую страницу плейлистов с другими query‑параметрами, оптимистичное обновление не сработает 😢
selectCachedArgsForQuery извлекает аргументы запросов, закэшированных в Redux Store.
Это позволяет получить все аргументы для запросов, имеющих активные кэши. Это полезно для отслеживания того,
какие запросы были сделаны и с какими параметрами, и для повторного использования этих параметров при необходимости: