RTK Query предоставляет два основных подхода к выполнению запросов:
автоматический через useQuery и ручной через
useLazyQuery. Второй вариант предназначен для сценариев,
где запрос не должен выполняться сразу при рендере компонента, а
запускается по событию или по явно заданному условию. Это позволяет
полностью контролировать момент и частоту обращения к API, сохраняя при
этом все преимущества кэширования, дедупликации и статуса запроса,
которые предоставляет RTK Query.
Хук useLazyQuery возвращает не результат запроса, а
триггер-функцию и объект состояния. Запрос не выполняется автоматически
при инициализации компонента. Вместо этого разработчик получает функцию,
которую можно вызвать в любой момент.
Типичная сигнатура выглядит следующим образом:
const [trigger, result] = useLazyGetUserQuery();
Где:
trigger — функция запуска запросаresult — объект состояния запроса (данные, ошибки,
статус)Ключевое отличие от useQuery заключается в отсутствии
автоматического выполнения запроса.
Ручной запуск запроса используется, когда данные нужны только при определённом действии пользователя.
const [fetchUser, { data, error, isFetching }] = useLazyGetUserQuery();
const handleClick = () => {
fetchUser(1);
};
В данном примере запрос к серверу выполняется только после вызова
handleClick. Пока функция не вызвана, запрос не
отправляется, а состояние остаётся в начальном виде.
Параметры передаются непосредственно в функцию-триггер. Они
аналогичны аргументам useQuery.
const [getPost, { data }] = useLazyGetPostQuery();
getPost({ postId: 42 });
В зависимости от определения endpoint, параметры могут быть объектом, строкой или числом. RTK Query использует их для формирования cache key, обеспечивая корректное кэширование результатов.
Объект состояния, возвращаемый useLazyQuery, включает
все стандартные поля RTK Query:
data — результат запросаerror — ошибка выполненияisFetching — идёт ли запросisSuccess — успешное завершениеisError — наличие ошибкиstatus — текущий статус (uninitialized,
pending, fulfilled,
rejected)Особенность useLazyQuery заключается в начальном
состоянии uninitialized, которое означает, что запрос ещё
не запускался.
const [loadData, result] = useLazyGetDataQuery();
// result.status === "uninitialized"
После вызова триггера состояние изменяется на pending, а
затем на fulfilled или rejected.
Функция trigger может вызываться многократно. Каждый
вызов инициирует новый запрос, если параметры изменились или если не
используется кэш.
trigger(1);
trigger(2);
trigger(1);
RTK Query применяет стратегию кэширования, поэтому повторные запросы
с одинаковыми параметрами могут возвращать данные из кэша без обращения
к серверу, в зависимости от конфигурации keepUnusedDataFor
и политики refetch.
Функция trigger возвращает Promise, что
позволяет работать с результатом запроса напрямую.
const [fetchUser] = useLazyGetUserQuery();
const handleLoad = async () => {
try {
const result = await fetchUser(1).unwrap();
console.log(result);
} catch (err) {
console.error(err);
}
};
Метод unwrap() извлекает полезные данные или выбрасывает
ошибку, что упрощает обработку асинхронных операций.
Основное различие между useQuery и
useLazyQuery заключается в стратегии запуска:
useQuery — автоматический запуск при рендереuseLazyQuery — ручной запуск через triggerДополнительно:
useLazyQuery не делает запрос до вызова triggeruseQuery всегда связан с жизненным циклом
компонентаuseLazyQuery удобен для событийных сценариевПример различия:
// автоматический запрос
const { data } = useGetUserQuery(1);
// ручной запрос
const [getUser, { data }] = useLazyGetUserQuery();
RTK Query не сбрасывает состояние автоматически при повторных вызовах
trigger. Однако существует возможность сброса через
reset в некоторых реализациях или при размонтировании
компонента.
const [loadData, result] = useLazyGetDataQuery();
result.reset();
Сброс возвращает состояние к uninitialized.
useLazyQuery часто используется для UI, где данные
запрашиваются только при взаимодействии:
Пример поиска:
const [search, { data }] = useLazySearchQuery();
const handleSearch = (text) => {
search(text);
};
Запрос выполняется только после подтверждения, что снижает количество сетевых операций.
Каждый вызов trigger рассматривается как независимая
операция. RTK Query формирует cache key на основе аргументов, что
позволяет:
trigger({ id: 1 });
trigger({ id: 1 }); // может использовать кэш
trigger({ id: 2 }); // новый запрос
Поскольку useLazyQuery не привязан к автоматическому
запуску, его состояние менее зависимо от рендера. Это особенно важно
при:
Запрос остаётся под контролем логики приложения, а не жизненного цикла React-компонента.
Несмотря на ручной запуск, useLazyQuery полностью
интегрирован в систему RTK Query:
const api = createApi({
endpoints: (builder) => ({
getUser: builder.query({
query: (id) => `/user/${id}`,
providesTags: ['User']
})
})
});
После инвалидации тегов следующий вызов trigger получит
актуальные данные.
useLazyQuery особенно полезен в следующих архитектурных
паттернах:
Пример формы:
const [loadOptions, { data }] = useLazyGetOptionsQuery();
useEffect(() => {
if (selectedValue) {
loadOptions(selectedValue);
}
}, [selectedValue]);
Если компонент размонтируется до завершения запроса, RTK Query продолжает выполнение запроса, но результат будет обработан только если компонент ещё подписан на состояние. Это позволяет избегать утечек данных в UI.
useLazyQuery использует тот же middleware слой, что и
обычные запросы RTK Query. Это означает:
Разница существует только на уровне триггера выполнения.
Поскольку trigger можно вызывать вручную, важно
контролировать частоту запросов. Часто используется debounce или
throttle:
const [search] = useLazySearchQuery();
const handleInput = (value) => {
debounce(() => search(value), 300);
};
Это снижает нагрузку на сервер и предотвращает избыточные запросы.
Ошибки обрабатываются аналогично useQuery, но
дополнительно могут быть обработаны через unwrap():
try {
await trigger(1).unwrap();
} catch (e) {
console.log('Ошибка загрузки');
}
Состояние isError и объект error
обновляются синхронно с результатом запроса.
В реальных приложениях useLazyQuery часто комбинируется
с:
useMutationuseEffectЭто позволяет строить полностью управляемые потоки данных, где запросы выполняются строго по бизнес-логике, а не автоматически при рендере компонентов.