Batching запросов в TanStack Query — это механизм группировки нескольких операций обновления состояния запросов в единый цикл рендера или единый пакет сетевых и UI-обновлений, позволяющий снизить количество лишних перерисовок, уменьшить нагрузку на React и ускорить обработку связанных запросов.
TanStack Query сам по себе не реализует сетевой batching (объединение HTTP-запросов в один), однако предоставляет и опирается на batching обновлений состояния через React и внутренний query cache. Это особенно важно в сценариях, где одновременно изменяется несколько query или происходит каскадное обновление данных после мутаций.
Внутри TanStack Query batching проявляется в двух формах:
Основная цель — объединить несколько вызовов setState, invalidateQueries, setQueryData и подобных операций в один цикл обновления UI.
Ключевой эффект:
TanStack Query не изолирован от механизма React batching. В современных версиях React (18+) используется автоматический batching, который объединяет обновления, происходящие внутри:
Пример поведения:
queryClient.setQueryData(['user', 1], user);
queryClient.invalidateQueries({ queryKey: ['orders'] });
queryClient.setQueryData(['settings'], settings);
В React 18 эти три операции чаще всего приводят к одному render-циклу, а не к трём отдельным.
TanStack Query использует QueryCache как центральный механизм хранения и распространения изменений.
Каждое изменение:
проходит через уведомления подписчиков.
Чтобы избежать каскадного уведомления для каждой операции, библиотека группирует их в единый flush-процесс.
Это предотвращает ситуацию, когда UI реагирует на каждое промежуточное состояние cache.
Одна из самых частых причин множественных обновлений — инвалидирование нескольких ключей.
queryClient.invalidateQueries({ queryKey: ['user'] });
queryClient.invalidateQueries({ queryKey: ['posts'] });
queryClient.invalidateQueries({ queryKey: ['comments'] });
Без batching это могло бы вызвать:
Внутренне TanStack Query старается объединять такие вызовы в один проход планировщика обновлений.
Важно различать batching обновлений и сетевой batching.
TanStack Query не объединяет HTTP-запросы автоматически, но batching влияет на то, когда эти запросы стартуют.
Пример:
queryClient.invalidateQueries({ queryKey: ['user'] });
queryClient.invalidateQueries({ queryKey: ['posts'] });
Вместо мгновенного запуска двух независимых рефетчей возможен сценарий:
Это даёт эффект “псевдо-batching” на уровне сети.
Каждый useQuery подписывается на изменения конкретного query.
const { data: user } = useQuery({
queryKey: ['user', id],
queryFn: fetchUser
});
const { data: posts } = useQuery({
queryKey: ['posts', id],
queryFn: fetchPosts
});
Если одновременно происходят изменения:
без batching это привело бы к двум render-циклам компонента.
С batching:
TanStack Query использует планировщик, основанный на microtask queue.
Общий принцип:
Это критично для следующих сценариев:
Мутации часто вызывают серию связанных обновлений cache:
mutation.mutate(data, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['list'] });
queryClient.invalidateQueries({ queryKey: ['stats'] });
queryClient.setQueryData(['lastUpdate'], Date.now());
}
});
Без batching:
С batching:
setQueryData — одна из самых “частых” операций, приводящих к множественным обновлениям.
queryClient.setQueryData(['cart'], old => [...old, item]);
queryClient.setQueryData(['cartCount'], c => c + 1);
TanStack Query группирует такие изменения, если они происходят в одном синхронном контексте, минимизируя количество уведомлений подписчиков.
Batching имеет ограничения и не гарантируется в следующих случаях:
queryClient.setQueryData(['a'], 1);
setTimeout(() => {
queryClient.setQueryData(['b'], 2);
}, 0);
Здесь batching не объединяет операции, так как они происходят в разных event loop циклах.
Некоторые внутренние операции могут вызывать немедленное уведомление подписчиков, например:
Если обновление вызывает внешние события (например, websocket sync), batching может быть частично обойдён ради консистентности данных.
Эффективное использование batching достигается за счёт:
queryClient.setQueryData(['user', id], user);
queryClient.setQueryData(['profile', id], profile);
queryClient.invalidateQueries({ queryKey: ['dashboard'] });
вместо разнесения по разным async шагам.
Частые последовательные setQueryData без необходимости приводят к:
Логика мутаций может быть структурирована как единый блок:
в одном синхронном контексте, чтобы batching сработал максимально эффективно.
TanStack Query Devtools визуализируют каждое изменение cache, что иногда создаёт иллюзию отсутствия batching.
На самом деле:
Это различие важно при профилировании производительности.
Batching влияет на три уровня:
React rendering
Query processing
Network scheduling
Разделение операций на разные microtasks без необходимости приводит к потере batching.
Частое инвалидирование больших групп ключей вызывает:
Ожидание, что TanStack Query объединит HTTP-запросы, приводит к архитектурным ошибкам. Библиотека не выполняет request deduplication на уровне payload.
Внутренне поведение можно представить как конвейер:
Эта модель обеспечивает баланс между реактивностью и производительностью, минимизируя лишние вычисления и сохраняя предсказуемость состояния приложения.