React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
Redux Essentials 21. RTK Query. Разделение API-слайса

Проект: https://codesandbox.io/s/redux-essentials-rtk-query-4-react-junior-1h2or8?file=/src/features/users/usersSlice.js

apiSlice уже сильно разросся, пора его разделить.

RTK Query предоставляет метод injectEndpoints. С его помощью мы можем вынести всю работу с пользователями в отдельный файл - в usersSlice.

1. Вызываем метод api.injectEndpoints и передаем ему набор эндпоинтов.
2. Метод вернет обновленный объект extendedApiSlice - это тот же самый apiSlice, эндпоинты добавлены через мутацию.
3. Далее мы можем использовать extendedApiSlice вместо apiSlice для ясности.
4. Удаляем getUsers из apiSlice.
5. Отдельно экспортируем эндпоинт: extendedApiSlice.endpoints.getUsers, чтобы было удобнее инициировать его в файле index.js.

#redux #управлениесостоянием #обменданными #rtkquery #документация #примерыкода
👍1
Redux Essentials 22. RTK Query + EntityAdapter

Возвращаемся к EntityAdapter в userSlice. Все оказалось очень просто: при получении данных запроса в transformResponse просто вызываем метод usersAdapter.setAll, чтобы сохранить в нужном виде.

https://codesandbox.io/s/redux-essentials-rtk-query-5-react-junior-b5ebze?file=/src/features/users/usersSlice.js

const usersAdapter = createEntityAdapter();
const initialState = usersAdapter.getInitialState();

getUsers: builder.query({
// ...
transformResponse: function (responseData) {
return usersAdapter.setAll(initialState, responseData);
}
})

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

// получение данных запроса из apiSlice
export const selectUsersResult = getUsersEndpoint.select();

// извлечение данных из запроса, если он выполнен
// результат работы entity adapter ({ids, entities}) или undefined
const selectUsersData = createSelector(
selectUsersResult,
function (usersResult) {
return usersResult.data;
}
);

export const {
selectAll: getUsers,
selectById: getUserById
} = usersAdapter.getSelectors(
function(state) {
return selectUsersData(state) ?? initialState;
}
);

#redux #управлениесостоянием #обменданными #rtkquery #документация #примерыкода
👍2
RTK Query. Query hook: параметры

В хук запроса, сгенерированный для эндпоинта, можно передать параметры:

- queryArg (параметр для генерации урла запроса, передается в функцию query)
- queryOptions (объект с настройками).

Настройки есть следующие:

- skip - пропустить выполнение запроса для текущего рендера.
- pollingInterval - для автоматического перезапроса данных.
- selectFromResult - позволяет изменить возвращаемое хуком значение (оптимизировано для рендера).
- refetchOnMountOrArgChange
- refetchOnFocus - refetch при возвращении фокуса на вкладку браузера.
- refetchOnReconnect - refetch при восстановлении сетевого соединения, если оно было потеряно.

#обменданными #rtkquery #документация
👍1
Redux Essentials 23. Выбор данных из запроса

Теперь займемся страницей пользователя, на которой выводятся отфильтрованные по автору посты.

https://codesandbox.io/s/redux-essentials-rtk-query-6-react-junior-kfzw2t?file=/src/features/users/UserPage.js

Воспользуемся хуком useGetPostsQuery, чтобы получить полный список постов и настройкой selectFromResult (параметры хука запроса), чтобы отфильтровать статьи по автору.

const { postsForUser } = useGetPostsQuery(undefined, {
selectFromResult: function(result) {
return {
...result,
postsForUser: (result.data || []).filter(function(post) {
return post.user === userId
})
}
}
});


При любом изменении state массив postsForUser будет пересоздаваться, вызывая перерендер компонента, поэтому нужно обернуть извлечение данных в мемоизированный селектор.

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
Трансформация данных запроса: 3 подхода

Мы получаем данные из API в одном виде, но для использования может потребоваться их изменение. Сделать это можно в трех местах:

1. Сразу после получения. Преобразуем ответ с помощью transformResponse и сохраняем в кеше уже измененные данные. Имеет смысл, если новый формат подходит всем потребителям. Можно сочетать с entity adapter.

2. В компоненте. Получаем данные, сохраненные в кеше, и извлекаем из них то, что нужно. Рекомендуется использовать useMemo для избежания ненужных перерендерингов. Полезно, если одному компоненту нужны данные в другом формате.

3. В хуке запроса. Отбираем нужные данные с помощью опции selectFromResult. Полезно, если компоненту нужна только часть данных.

#redux #rtkquery #обменданными #паттерны
👍2
Redux Essentials 24. RTK Query. Оптимистичные обновления

Переходим к реакциям на статьи.

https://codesandbox.io/s/redux-essentials-rtk-query-7-react-junior-uzedjr?file=/src/features/api/apiSlice.js

Добавляем эндпоинт-мутацию addReaction.

После того, как пользователь поставил реакцию, нужно отправить запрос и обновить данные статьи, чтобы показать изменения.

addReaction: builder.mutation({
query: function({ postId, reaction }) {},
invalidatesTags: function(result, error, arg) {
return [
{ type: 'Post', id: arg.postId }
];
}
})


Но ради такого мелкого изменения не хочется по новой запрашивать целый список. Для таких ситуаций можно воспользоваться техникой "оптимистичного обновления".

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

Для эндпоинта можно указать метод onQueryStarted. Это функция, которая при вызове получит два параметра - arg и thunkApi, в котором нас интересуют два метода - dispatch для отправки экшена и queryFulfilled - промис отправленного запроса.

Таким образом, запрос отправляется, но мы не хотим ждать его результата, а хотим внести изменения немедленно. У apiSlice есть свойство utils, а в нем метод updateQueryData. Он позволяет изменить ранее сохраненные данные. Для этого нужно указать название эндпоинта, ключ кеширования (если нужно) и собственно функцию для обновления.

const patchThunk = apiSlice.util.updateQueryData('getPosts', undefined, function(draft) {
// ... вносим изменения в данные draft
// данные можно мутировать
});


Теперь нужно только задиспатчить этот thunk, чтобы изменения отобразились в компоненте.

const patchResult = thunkApi.dispatch(patchThunk);


Если же запрос на сервер (queryFulfilled) не выполнится, патч можно отменить, вызвав метод patchResult.undo().

Осталось только внести изменения в компонент ReactionButton, чтобы он использовал хук новой мутации.

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
Redux Essentials 25. RTK Query. Потоковое обновление кеша

И наконец, осталось только переделать уведомления.

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

https://codesandbox.io/s/redux-essentials-rtk-query-7-react-junior-uzedjr?file=/src/features/notifications/notificationsSlice.js

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

Создание эндпоинта

Создадим новый эндпоинт getNotifications, который будет получать исходный список. Для удобства вынесем его в файл notificationsSlice.js и подключим с помощью метода apiSlice.injectEndpoints.

Экспортируем хук useGetNotificatinsQuery и используем его в компоненте NotificationsList. Тут ничего нового.

Подписка на новые уведомления

У эндпоинта есть метод жизненного цикла onCacheEntryAdded. Он вызывается в тот момент, когда эндпоинт добавляется в кеш, то есть для него "бронируется" там место (данные при этом еще не получены).

Внутри этого метода мы можем подписаться на сокет-канал.

Метод принимает два параметра: arg и thunkApi. В последнем есть несколько полезных методов и данных:

const {
updateCachedData,
cacheDataLoaded,
cacheEntryRemoved,
dispatch
} = thunkApi;


Последовательность действий:

- ждем, пока выполнится основной запрос (cacheDataLoaded)
- подписываемся на сокет-канал, при получении новых уведомлений добавляем их к кешированным ранее данным (updateCachedData)
- отслеживаем удаление кеша (cacheEntryRemoved), закрываем канал

Непрочитанные уведомления

Теперь список уведомлений хранится в apiSlice. Но нам еще нужно хранить их состояние (непрочитанные/прочитанные). Используем для этого уже существующий notificationsSlice, только вместо самих уведомлений поместим туда данные об их состоянии.

При добавлении новых уведомлений в кеш, нужно добавлять их и в notificationsSlice. Добавление происходит при выполнении базового запроса (экшен extendedApi.endpoints.getNotifications.matchFulfilled), а также при получении новых данных через сокет. Для второго случая создадим отдельный экшен notificationsReceived (используем утилитарную функцию createAction).

В extraReducers отслеживаем нужные экшены (builder.addMatcher) и добавляем новые элементы в список.

Осталось только в компоненте NotificationsList получить состояние уведомлений и отметить их как нужно.

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
RTK Query. Резюме

Небольшое подведение итогов по RTK Query после прохождения руководства.

RTK Query работает поверх Redux Toolkit, используя его основные концепции.

Предполагается, что вся работа с API в проекте будет собрана в отдельный слайс, созданный с помощью функции createApi. Тут можно настроить, как именно должен выполняться запрос (`baseQuery`), а также указать набор эндпоинтов (`endpoints`).

Эндпоинт может быть запросом (query) или мутацией (mutation).

У каждого эндпоинта есть поле query, формирующее урл запроса. Полученный ответ можно обработать в методе transformResponse. Кроме того, можно форматировать данные прямо в компоненте (три подхода к форматированию данных)

При необходимости, часть эндпоинтов можно перенести в отдельный файл и подключить к слайсу через apiSlice.injectEndpoints (разделение эндпоинтов).

Все полученные данные хранятся в кеше. Если на эндпоинт нет подписок, его кеш через некоторое время сбрасывается.

Каждый эндпоинт можно пометить тегом (строка или объект), который можно инвалидировать (принудительно сбросить кеш и вызвать перезапуск запроса) в другом эндпоинте (уточненные теги).

Для эндпоинта генерируется хук (хуки запросов https://yangx.top/react_junior/342), который можно использовать в компонентах. Результат работы хука помимо данных содержит информацию о статусе запроса (`isFetching`, isLoading, `isError`). Кроме того, хук может принимать параметры для автоматического перезапроса данных и для выборки нужных данных из кеша (`selectFromResult`) (параметры хука запроса).

Можно работать с эндпоинтами и без хуков, они предоставляют ряд методов (`initiate()`, `select()`) (ручная работа с эндпоинтами).

RTK Query поддерживает оптимистичные обновления кеша, для этого используем метод жизненного цикла onQueryStarted и метод обновления apiSlice.util.updateQueryData.

Кроме того, возможно обновление кеша "по необходимости", например, при подписке на сокет-канал (потоковое обновление кеша). Для этого используем метод жизненного цикла onCacheEntryAdded.

#redux #rtkquery #обменданными
👍4
RTK Query

Освежаю знания об RTK Query

RTK Query - это часть Redux Toolkit, которая предназначена для удобной работы с API. Она собирает все, что относится к API в одном месте и дает нам много приятного синтаксического сахара.

Заводим для API отдельный слайс, где будут лежать все данные, полученные с сервера. Но не обычный слайс, который createSlice, а прокачанный слайс, который createApi.
Подключить этот api-слайс к стору посложнее, чем обычный, тут нужно и редьюсер добавить, и специальный миддлвар.

В api-слайсе определяем функцию, которая будет осуществлять запросы - baseQuery.

На этом техническая подготовка заканчивается и начинается собственно логика обмена данными - поле endpoints. Тут мы получаем builder и с его помощью (`builder.query`) создаем массив наших эндпоинтов (есть много разных настроек) . Каждый эндпоинт на выходе из слайса создает себе личный хук , который можно дернуть в любом месте приложения. А в этом хуке уже предусмотрена куча удобных поле - data, isLoading, error и т.п. И дополнительно можно закинуть ряд настроек, вроде селектора . То есть вытаскивать данные из API со всеми удобствами мы уже можем. Можно даже сочетать все это дело с EnitityAdapter.

Примечание: можно выполнить запрос и «вручную» без использования хука.

Отправка данных на сервер работает практически так же, как и вытягивание, только эндпоинт нужно немного по-другому оформить (через `builder.mutation`). Хук тоже немного другой.

Самое приятное, что запросы к серверу кешируются, однако кеш иногда приходится сбрасывать. Это можно сделать напрямую руками, с помощью метода refetch, который есть в хуках. Но лучше настроить зависимости с помощью системы тегов, чтобы RTK Query сбрасывала то, что нужно, самостоятельно.

Конечно, api-слайс можно расчленять (injectEndpoints).

RTK Query предлагает продвинутые техники работы с API, например, оптимистичное обновление, когда данные меняются сразу, а запрос выполняется уже в фоне. Еще есть возможность подписаться на источник событий (сокет-канал) , чтобы получать данные из него.

Предыдущее резюме по RTK Query, с большим количество технических подробностей.

#управлениесостоянием #rtkquery #обменданными
🔥3👍2