В приложениях интерфейс часто должен отображать максимально актуальные данные:
Постоянный ручной вызов refetch() создаёт лишний код и
усложняет поддержку. В TanStack Query для подобных задач предусмотрены
автоматические механизмы повторного получения данных.
Два наиболее важных параметра:
refetchIntervalrefetchOnWindowFocusОни отвечают за разные сценарии обновления:
| Параметр | Назначение |
|---|---|
refetchInterval |
Периодический опрос сервера через заданный интервал |
refetchOnWindowFocus |
Обновление данных при возвращении пользователя к вкладке |
refetchIntervalrefetchInterval запускает автоматический повторный
запрос через указанный промежуток времени.
Пример:
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
refetchInterval: 5000
})
В данном случае:
Механизм выглядит следующим образом:
Интервал задаётся в миллисекундах.
| Значение | Интервал |
|---|---|
1000 |
1 секунда |
5000 |
5 секунд |
30000 |
30 секунд |
60000 |
1 минута |
Пример:
refetchInterval: 10000
Запрос будет повторяться каждые 10 секунд.
useQueryБазовый пример:
import { useQuery } from '@tanstack/react-query'
function Notifications() {
const query = useQuery({
queryKey: ['notifications'],
queryFn: async () => {
const response = await fetch('/api/notifications')
if (!response.ok) {
throw new Error('Ошибка загрузки')
}
return response.json()
},
refetchInterval: 5000
})
if (query.isLoading) {
return <div>Загрузка...</div>
}
if (query.isError) {
return <div>Ошибка</div>
}
return (
<ul>
{query.data.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
)
}
По умолчанию TanStack Query отключает интервальные запросы, если вкладка браузера неактивна.
Это важная оптимизация:
refetchIntervalInBackgroundЕсли необходимо продолжать обновление даже в фоне, используется:
refetchIntervalInBackground: true
Пример:
useQuery({
queryKey: ['prices'],
queryFn: fetchPrices,
refetchInterval: 1000,
refetchIntervalInBackground: true
})
Теперь запросы будут выполняться даже при неактивной вкладке.
Подходящие сценарии:
Неудачные сценарии:
Очень важно понимать взаимодействие refetchInterval и
staleTime.
Пример:
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 60000,
refetchInterval: 5000
})
Несмотря на staleTime: 60000, запрос всё равно будет
выполняться каждые 5 секунд.
Причина:
staleTime управляет «устареванием» кеша;refetchInterval запускает запрос принудительно по
таймеру.refetchInterval не зависит от freshness-статуса
данных.
TanStack Query позволяет задавать функцию вместо числа.
Пример:
useQuery({
queryKey: ['task'],
queryFn: fetchTask,
refetchInterval: (data) => {
if (data?.status === 'finished') {
return false
}
return 2000
}
})
Особенности:
false полностью отключает polling.Очень распространённый сценарий:
taskId;Пример:
useQuery({
queryKey: ['task-status', taskId],
queryFn: () => fetchTaskStatus(taskId),
refetchInterval: (data) => {
if (data?.completed) {
return false
}
return 3000
}
})
Polling часто требуется только в определённых условиях.
Пример:
useQuery({
queryKey: ['messages'],
queryFn: fetchMessages,
enabled: isAuthorized,
refetchInterval: isChatOpen ? 2000 : false
})
Здесь:
Слишком маленький интервал способен создать серьёзную нагрузку.
Проблемы:
| Тип данных | Рекомендуемый интервал |
|---|---|
| Чат | 1–3 секунды |
| Уведомления | 5–15 секунд |
| Метрики | 2–10 секунд |
| Статистика | 30–60 секунд |
| Справочные данные | Без polling |
refetchOnWindowFocusКогда пользователь возвращается к вкладке браузера, данные могут устареть.
TanStack Query умеет автоматически выполнять обновление в момент фокуса окна.
Пример:
useQuery({
queryKey: ['profile'],
queryFn: fetchProfile,
refetchOnWindowFocus: true
})
По умолчанию параметр уже включён:
refetchOnWindowFocus: true
Это означает:
Типичные сценарии:
Библиотека отслеживает:
visibilitychangefocusПосле возвращения вкладки в активное состояние запускается проверка необходимости refetch.
Важно понимать:
refetchOnWindowFocus обычно срабатывает только для
stale-данных.
Пример:
useQuery({
queryKey: ['settings'],
queryFn: fetchSettings,
staleTime: 60000,
refetchOnWindowFocus: true
})
Если пользователь вернулся к вкладке через 10 секунд, повторного запроса не будет.
Причина:
Можно заставить запрос выполняться всегда:
refetchOnWindowFocus: 'always'
Пример:
useQuery({
queryKey: ['feed'],
queryFn: fetchFeed,
refetchOnWindowFocus: 'always'
})
Теперь refetch будет происходить при каждом возвращении к вкладке.
refetchOnWindowFocus: false
Пример:
useQuery({
queryKey: ['countries'],
queryFn: fetchCountries,
refetchOnWindowFocus: false
})
Подходящие случаи:
refetchInterval и
refetchOnWindowFocusОба механизма могут работать одновременно.
Пример:
useQuery({
queryKey: ['dashboard'],
queryFn: fetchDashboard,
refetchInterval: 10000,
refetchOnWindowFocus: true
})
Поведение:
Если интервал слишком маленький:
refetchInterval: 1000
то refetchOnWindowFocus часто становится бессмысленным,
поскольку данные и так почти всегда свежие.
| Механизм | Когда срабатывает |
|---|---|
refetchInterval |
По таймеру |
refetchOnWindowFocus |
При возвращении к вкладке |
Если запрос завершился ошибкой:
Пример:
useQuery({
queryKey: ['server'],
queryFn: fetchServer,
refetchInterval: 5000,
retry: 3
})
Автоматические refetch-механизмы позволяют:
Но чрезмерное количество запросов приводит к обратному эффекту:
refetchInterval: 5000
refetchOnWindowFocus: true
staleTime: Infinity
refetchOnWindowFocus: false
refetchInterval: 2000
refetchIntervalInBackground: true
staleTime: 300000
refetchOnWindowFocus: true
Плохой пример:
refetchInterval: 100
Такой код способен:
Ненужный пример:
useQuery({
queryKey: ['countries'],
queryFn: fetchCountries,
refetchInterval: 5000
})
Список стран почти не меняется, поэтому polling здесь бессмысленен.
Иногда polling остаётся после отладки:
refetchInterval: 1000
В production это может привести к тысячам лишних запросов в минуту.
Параметры можно задавать глобально через
QueryClient.
Пример:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchInterval: 10000
}
}
})
Эти настройки применятся ко всем запросам по умолчанию.
Локальные настройки имеют приоритет:
useQuery({
queryKey: ['chat'],
queryFn: fetchChat,
refetchInterval: 2000
})
Даже если глобально установлен другой интервал, конкретный запрос будет использовать локальное значение.