RTK Query — это инструмент для получения, кеширования и синхронизации данных с сервером, входящий в состав библиотеки Redux Toolkit. Он решает одну из самых сложных задач фронтенд-разработки: управление серверным состоянием.
Под серверным состоянием понимаются данные, которые:
Типичные примеры:
До появления RTK Query разработчики обычно писали большое количество однотипного кода:
const fetchUsers = () => async (dispatch) => {
dispatch(setLoading(true))
try {
const response = await fetch('/api/users')
const data = await response.json()
dispatch(setUsers(data))
} catch (error) {
dispatch(setError(error.message))
} finally {
dispatch(setLoading(false))
}
}
При увеличении количества API-запросов приложение быстро обрастало:
RTK Query автоматизирует практически все эти задачи.
RTK Query строится вокруг концепции декларативной работы с API.
Разработчик описывает:
Всё остальное библиотека берёт на себя:
Redux отлично справляется с клиентским состоянием:
Но серверное состояние имеет другую природу.
Данные загружаются не мгновенно.
Необходимо хранить:
{
data: [],
isLoading: true,
error: null
}
Серверные данные могут устаревать.
Например:
Если два компонента запрашивают один и тот же список пользователей, нет смысла выполнять два одинаковых HTTP-запроса.
После создания нового комментария список комментариев должен автоматически обновиться.
Необходимо учитывать:
Ручная реализация подобных механизмов превращается в сложную инфраструктуру.
RTK Query предоставляет готовое решение.
До RTK Query экосистема Redux обычно использовала:
Для работы с серверным состоянием популярность получили отдельные библиотеки:
Команда Redux Toolkit решила встроить аналогичное решение непосредственно в Redux-экосистему.
Так появился RTK Query.
Без RTK Query для каждого запроса обычно создаются:
RTK Query избавляет от этой рутины.
Вместо десятков файлов:
getUsers: builder.query({
query: () => '/users'
})
RTK Query автоматически кеширует ответы.
Если один и тот же запрос вызывается повторно:
useGetUsersQuery()
библиотека:
Если несколько компонентов одновременно вызывают одинаковый endpoint:
useGetUsersQuery()
RTK Query выполнит только один HTTP-запрос.
Все компоненты получат общий результат.
Каждый query автоматически предоставляет:
const {
data,
error,
isLoading,
isFetching,
isSuccess,
isError
} = useGetUsersQuery()
Не требуется создавать reducer вручную.
После изменения данных RTK Query умеет автоматически обновлять связанные запросы.
Пример:
addPost: builder.mutation({
query: (body) => ({
url: '/posts',
method: 'POST',
body
}),
invalidatesTags: ['Posts']
})
После создания записи:
RTK Query автоматически создаёт hooks:
const { data } = useGetUsersQuery()
и mutations:
const [createUser] = useCreateUserMutation()
Разработчику не нужно писать их вручную.
RTK Query состоит из нескольких ключевых частей.
Главный объект RTK Query — API Slice.
Создаётся через:
createApi()
Пример:
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: '/api'
}),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => '/users'
})
})
})
API Slice:
Endpoint описывает конкретный запрос.
Существует два типа endpoint:
Для получения данных:
builder.query()
Примеры:
Для изменения данных:
builder.mutation()
Примеры:
Base Query — базовый механизм выполнения запросов.
Стандартный вариант:
fetchBaseQuery()
Это обёртка над Fetch API.
Пример:
baseQuery: fetchBaseQuery({
baseUrl: '/api'
})
baseUrl: '/api'
prepareHeaders: (headers) => {
headers.set('Authorization', `Bearer ${token}`)
return headers
}
RTK Query автоматически:
Ошибки автоматически преобразуются в единый формат.
Когда компонент вызывает:
useGetUsersQuery()
RTK Query выполняет следующие действия:
Кеш — одна из главных возможностей библиотеки.
Каждый запрос получает уникальный cache key.
Например:
useGetPostQuery(5)
Ключ может выглядеть так:
getPost(5)
RTK Query хранит:
{
queries: {
'getPost(5)': {
data: {...},
status: 'fulfilled'
}
}
}
Если разные компоненты используют одинаковый query:
useGetPostQuery(5)
они получают общие данные.
Это снижает:
RTK Query удаляет неиспользуемые данные не сразу.
Настройка:
keepUnusedDataFor: 60
означает:
RTK Query умеет автоматически обновлять данные.
refetchOnFocus: true
Когда пользователь возвращается во вкладку:
refetchOnReconnect: true
После восстановления интернета:
Регулярный автоматический refetch:
pollingInterval: 5000
Запрос будет выполняться каждые 5 секунд.
Это одна из самых важных особенностей RTK Query.
isLoading === true
означает:
Обычно используется для initial loader.
isFetching === true
означает:
Интерфейс может продолжать показывать старые данные без loader reset.
Mutation используются для изменения данных.
Пример:
createPost: builder.mutation({
query: (post) => ({
url: '/posts',
method: 'POST',
body: post
})
})
Использование:
const [createPost, result] =
useCreatePostMutation()
RTK Query автоматически даёт:
const [
createPost,
{
data,
error,
isLoading,
isSuccess
}
]
RTK Query поддерживает optimistic update.
Суть подхода:
Это создаёт ощущение мгновенного интерфейса.
Одно из важнейших понятий RTK Query — tags.
Query может объявить:
providesTags: ['Posts']
Это означает:
Mutation может объявить:
invalidatesTags: ['Posts']
После mutation:
Без tags разработчик должен вручную:
RTK Query делает это автоматически.
RTK Query не требует обязательной нормализации.
Это важное отличие от классического Redux.
Ранее разработчики часто создавали:
{
users: {
byId: {},
allIds: []
}
}
RTK Query допускает хранение данных в исходном виде.
Однако при необходимости можно использовать:
createEntityAdapter()
для нормализованных структур.
RTK Query особенно тесно интегрирован с React.
Библиотека автоматически генерирует hooks:
useGetUsersQuery()
useAddUserMutation()
Это создаёт очень компактный код.
dispatch(fetchUsers())
Нужно отдельно:
const { data } = useGetUsersQuery()
Практически вся инфраструктура скрыта внутри библиотеки.
RTK Query часто сравнивают с React Query.
Обе библиотеки поддерживают:
Лучше подходит для:
Лучше подходит для:
Где присутствует:
RTK Query хорошо показывает себя в:
Если приложение уже использует Redux:
RTK Query становится естественным продолжением архитектуры.
Не каждое приложение нуждается в RTK Query.
Например:
Иногда достаточно обычного fetch.
Если Redux отсутствует:
могут быть проще.
RTK Query создаёт отдельный slice:
{
api: {
queries: {},
mutations: {},
subscriptions: {},
provided: {}
}
}
Содержат:
Хранят:
RTK Query отслеживает:
RTK Query активно использует middleware.
Middleware отвечает за:
RTK Query поддерживает SSR.
Это особенно важно для:
Можно:
Хотя RTK Query ориентирован на HTTP, он поддерживает:
RTK Query отражает современную эволюцию frontend-разработки.
Главная идея:
серверное состояние — отдельная категория данных, требующая специализированного инструмента.
RTK Query предоставляет:
Вместо ручного управления запросами разработчик описывает структуру API, а библиотека берёт на себя инфраструктурные задачи работы с серверными данными.