В RTK Query жизненный цикл запроса описывается через набор предсказуемых состояний, которые позволяют точно понимать, на каком этапе находится получение данных, и как это отражается в UI и внутреннем кэше. Основная идея заключается в том, что каждый запрос проходит стадии инициализации, загрузки, успешного завершения или ошибки, а библиотека предоставляет готовые флаги и метаданные для каждой из них.
RTK Query опирается на единый поток состояния, который интегрирован в Redux store. Для каждого запроса формируется набор производных значений:
Эти значения отражают текущее состояние запроса и позволяют разделять первичную загрузку, фоновое обновление и завершённые состояния.
Внутренне состояние также связано с жизненным циклом Redux Toolkit Query endpoint:
pendingfulfilledrejectedОднако в приложениях чаще используются производные флаги, а не прямое обращение к lifecycle action.
Состояние loading в RTK Query связано с первым запросом
данных, когда кэш ещё не содержит результата.
Ключевой флаг:
Это состояние возникает, когда:
Типичный сценарий:
const { data, isLoading } = useGetUsersQuery();
При первом вызове:
data равно undefinedisLoading равно trueisFetching также trueОсобенность RTK Query заключается в том, что loading не
используется для повторных запросов, если данные уже существуют в
кэше.
Разделение важно:
isLoading — первичная загрузкаisFetching — любой сетевой запрос (включая
повторный)Таким образом, loading отражает именно отсутствие данных
и ожидание первого результата.
Хотя основная тема — loading, важно учитывать связанное состояние
isFetching.
const { data, isFetching } = useGetUsersQuery();
isFetching становится true:
В отличие от isLoading, это состояние не зависит от
наличия данных.
Пример различий:
| Ситуация | isLoading | isFetching |
|---|---|---|
| первый запрос | true | true |
| данные уже есть, идёт refetch | false | true |
| данные получены | false | false |
Это разделение позволяет строить UI без мигания контента при обновлениях.
Состояние успеха определяется флагом:
Это состояние устанавливается, когда запрос завершился без ошибок и данные успешно сохранены в кэше RTK Query.
const { data, isSuccess } = useGetUsersQuery();
Условия перехода в success:
Особенности:
isSuccess может оставаться true даже при
последующих refetch, если данные валидныВажно учитывать, что success не означает отсутствие сетевой активности. Он означает наличие валидного результата в кэше.
Состояние success тесно связано с наличием данных:
isSuccess === true почти всегда означает
data !== undefinedОднако возможны нюансы:
skipToken запрос не выполняется, success не
устанавливаетсяconst { data, isSuccess } = useGetUserByIdQuery(id);
При корректном выполнении:
data содержит нормализованный ответisSuccess сигнализирует о завершённом успешном
lifecycleСостояние ошибки определяется флагом:
и сопровождается объектом:
Ошибка устанавливается при завершении запроса с отклонением:
baseQueryПример:
const { error, isError } = useGetUsersQuery();
Структура error зависит от baseQuery (например, fetchBaseQuery):
{
status: 404,
data: { message: "Not found" }
}
или:
{
error: "FETCH_ERROR",
message: "Network request failed"
}
isError становится true только после
завершения запросаRTK Query допускает одновременное существование нескольких флагов, что отражает реальные сценарии сетевой работы.
isLoading: true
data: undefined
Первичная загрузка.
isSuccess: true
data: [...]
Нормальное состояние после получения ответа.
isFetching: true
isSuccess: true
data: [...]
Фоновое обновление данных без потери UI.
isError: true
error: {...}
data: previousData
Сохраняется последний валидный результат в кэше, несмотря на ошибку нового запроса.
RTK Query использует кэширование по ключу аргументов запроса. Это напрямую влияет на состояние loading.
При повторном вызове:
useGetUsersQuery()
если данные уже есть в кэше:
isLoading будет falseisFetching может быть true, если
выполняется обновлениеisSuccess останется trueЭто ключевое отличие от классических решений без кэша, где каждый запрос заново устанавливает loading.
При изменении параметров запроса:
useGetUserQuery(userId)
смена userId вызывает:
isLoading === trueПри этом старые данные могут оставаться доступными до завершения
нового запроса в зависимости от конфигурации
keepPreviousData.
Опция keepPreviousData изменяет поведение состояний:
useGetUsersQuery(page, {
keepPreviousData: true
});
При включении:
dataisLoading не активируется повторноisFetching для отражения обновленияЭто позволяет избегать “пустого экрана” при смене параметров.
При использовании skip или skipToken запрос
не выполняется:
useGetUserQuery(id, { skip: !id });
В этом случае:
isLoading === falseisFetching === falseisSuccess === falseisError === falseСостояние считается неинициализированным, так как запрос не был запущен.
RTK Query также предоставляет поле:
statusОно принимает значения:
uninitializedpendingfulfilledrejectedЭто низкоуровневое представление, которое обычно дублируется флагами:
isLoadingisSuccessisErrorСоответствие:
pending → loading/fetchingfulfilled → successrejected → errorПоведение можно представить как последовательность:
uninitializedpending → isLoadingfulfilled → isSuccessrejected → isErrorПри повторных запросах:
isFetching активируется без сброса successЭта модель позволяет строить интерфейсы, в которых загрузка, обновление и ошибки не конфликтуют между собой, а отображаются как независимые аспекты одного запроса.