RTK Query автоматически управляет жизненным циклом HTTP-запросов и
предоставляет набор готовых флагов состояния, позволяющих отслеживать
процесс выполнения запроса, повторную загрузку, ошибки и актуальность
данных. Управление состоянием загрузки является одной из ключевых
возможностей библиотеки, поскольку избавляет приложение от ручного
хранения флагов loading, error,
success и промежуточных состояний в Redux Store.
Каждый endpoint RTK Query формирует собственное состояние запроса, которое включает:
RTK Query использует внутренний state-механизм, позволяющий синхронизировать UI с состоянием сетевого взаимодействия без дополнительного reducer-кода.
Хук запроса возвращает объект со служебными полями:
const {
data,
error,
isLoading,
isFetching,
isSuccess,
isError,
isUninitialized
} = useGetUsersQuery()
Каждый флаг отражает определённый этап жизненного цикла запроса.
Флаг isUninitialized показывает, что запрос ещё ни разу
не запускался.
Это состояние характерно для:
skip;Пример:
const [fetchUser, result] = useLazyGetUserQuery()
console.log(result.isUninitialized)
До вызова fetchUser() значение будет:
true
После запуска запроса:
false
Флаг isLoading указывает на первую загрузку данных.
Он становится true только в момент первоначального
выполнения запроса, когда данные ещё отсутствуют в кэше.
Пример:
const { data, isLoading } = useGetPostsQuery()
Использование:
if (isLoading) {
return <Spinner />
}
Ключевая особенность:
isLoading === true
только если:
После получения данных флаг больше не становится true,
даже при повторных запросах.
Флаг isFetching показывает, что запрос выполняется прямо
сейчас, независимо от наличия данных.
Пример:
const { data, isFetching } = useGetPostsQuery()
Сценарий:
isFetching === trueisLoading === falseЭто особенно важно для UX.
Пример:
<>
{isFetching && <SmallLoader />}
<PostsList posts={data} />
</>
Старые данные продолжают отображаться, пока идёт обновление.
Отвечает за первую загрузку:
Нет данных + выполняется запрос
Отвечает за любую активную сетевую операцию:
Выполняется запрос
Даже если данные уже существуют.
isLoading = true
isFetching = true
data = undefined
isLoading = false
isFetching = false
data = [...]
isLoading = false
isFetching = true
data = [...]
Флаг isSuccess показывает успешное завершение
запроса.
Пример:
const { isSuccess } = useGetUsersQuery()
После успешного ответа:
isSuccess === true
Этот флаг часто используется для:
Пример:
if (isSuccess) {
return <UsersList />
}
Флаг isError становится true, если запрос
завершился ошибкой.
Пример:
const { error, isError } = useGetUsersQuery()
Использование:
if (isError) {
return <ErrorBlock error={error} />
}
RTK Query возвращает стандартизированный объект ошибки.
Пример для fetchBaseQuery:
{
status: 404,
data: {
message: 'User not found'
}
}
Использование:
if (error) {
return (
<div>
{error.status}
</div>
)
}
function UsersPage() {
const {
data,
error,
isLoading,
isFetching,
isError
} = useGetUsersQuery()
if (isLoading) {
return <FullPageLoader />
}
if (isError) {
return (
<ErrorMessage>
{error.status}
</ErrorMessage>
)
}
return (
<>
{isFetching && (
<TopProgressBar />
)}
<UsersTable users={data} />
</>
)
}
RTK Query сохраняет предыдущие данные в кэше.
Поэтому при повторной загрузке:
data
не очищается.
Это предотвращает:
RTK Query предоставляет два разных поля:
data
currentData
Содержит последние доступные данные.
Даже если запрос выполняется повторно.
Содержит данные только для текущего аргумента запроса.
Пример:
const {
data,
currentData
} = useGetUserQuery(userId)
При смене userId:
data -> старые данные
currentData -> undefined
Это позволяет избежать отображения устаревшего контента.
if (!currentData && isFetching) {
return <Skeleton />
}
Mutation-хуки имеют аналогичный набор флагов:
const [
createUser,
{
data,
error,
isLoading,
isSuccess,
isError
}
] = useCreateUserMutation()
isLoading = false
isSuccess = false
isError = false
isLoading = true
isSuccess = true
isError = true
Mutation предоставляет функцию reset.
Пример:
const [
login,
{ isSuccess, reset }
] = useLoginMutation()
Сброс состояния:
reset()
После сброса:
isSuccess = false
isError = false
data = undefined
RTK Query отлично сочетается с skeleton-интерфейсами.
Пример:
if (isLoading) {
return <UsersSkeleton />
}
Можно разделять глобальную и локальную загрузку.
Пример:
return (
<>
{isFetching && (
<LinearProgress />
)}
<Content data={data} />
</>
)
RTK Query автоматически выполняет повторные запросы.
Например:
refetchOnFocus: true
или:
refetchOnReconnect: true
Во время повторного запроса:
isFetching = true
Но:
isLoading = false
Каждый query-хук возвращает функцию refetch.
Пример:
const {
data,
refetch,
isFetching
} = useGetPostsQuery()
Использование:
<button onCl ick={refetch}>
Refresh
</button>
При использовании polling RTK Query периодически обновляет данные.
Пример:
useGetNotificationsQuery(undefined, {
pollingInterval: 5000
})
Каждые 5 секунд:
isFetching = true
Параметр skip отключает запрос.
Пример:
const { isUninitialized } = useGetUserQuery(id, {
skip: !id
})
Пока id отсутствует:
isUninitialized = true
RTK Query позволяет выбирать только нужные части состояния.
Пример:
const { isFetching } = useGetUsersQuery(undefined, {
selectFromResult: ({ isFetching }) => ({
isFetching
})
})
Это уменьшает количество ререндеров.
RTK Query формирует флаги автоматически на основе внутреннего статуса:
uninitialized
pending
fulfilled
rejected
На их основе строятся:
isLoadingisFetchingisSuccessisErrorТакже доступно поле:
status
Пример:
const { status } = useGetUsersQuery()
Возможные значения:
uninitialized
pending
fulfilled
rejected
if (isLoading) {
return <PageSkeleton />
}
if (isError) {
return <ErrorPage />
}
if (!data.length) {
return <EmptyState />
}
{isFetching && <RefreshingIndicator />}
<UsersTable users={data} />
RTK Query автоматически предотвращает множество проблем:
При изменении аргументов старые запросы корректно отслеживаются системой кэширования.
Настройка:
keepUnusedDataFor
влияет на сохранение кэша.
Пример:
keepUnusedDataFor: 60
Данные будут храниться 60 секунд после отписки компонента.
Если пользователь вернётся раньше:
isLoading = false
Поскольку данные уже находятся в кэше.
Можно вручную очищать кэш и состояние.
Пример:
dispatch(
api.util.resetApiState()
)
После сброса:
isUninitialized = true
Во время server-side rendering RTK Query также сохраняет статусы запросов.
После гидратации:
Redux DevTools позволяют наблюдать:
Типичные action:
api/executeQuery/pending
api/executeQuery/fulfilled
api/executeQuery/rejected
if (isLoading) {
return <PageLoader />
}
{isFetching && (
<MiniSpinner />
)}
if (isError) {
return <ErrorBanner />
}
<DataGrid data={data} />
Ошибка:
if (isLoading) {
return <Loader />
}
Проблема:
при refetch индикатор не отображается.
Ошибка:
if (isFetching) {
return <Loader />
}
Проблема:
UI исчезает при каждом обновлении.
Ошибка:
<UserCard user={data} />
При смене id могут отображаться устаревшие данные.
Использовать:
isLoading
Использовать:
isFetching
Использовать:
isError
Использовать:
isSuccess
Использовать:
isUninitialized