Одной из ключевых возможностей TanStack Query является автоматическая синхронизация серверного состояния. Библиотека самостоятельно определяет моменты, когда данные могут устареть, и инициирует повторные запросы без ручного вмешательства.
Автоматическое обновление данных решает несколько задач:
TanStack Query использует несколько встроенных механизмов обновления, которые работают независимо друг от друга и могут комбинироваться.
Центральным понятием автоматического обновления является состояние stale.
По умолчанию любые данные считаются устаревшими сразу после загрузки.
useQuery({
queryKey: ['users'],
queryFn: fetchUsers
})
После успешного выполнения запроса:
Это не означает мгновенный повторный запрос. Статус stale лишь говорит системе, что данные можно обновлять при наступлении определённых событий.
Для управления этим используется staleTime.
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 1000 * 60
})
В данном случае данные считаются свежими одну минуту.
Пока данные fresh:
После истечения времени данные становятся stale.
Стандартное поведение.
staleTime: 0
Данные устаревают мгновенно.
Подходит для:
staleTime: 1000 * 60 * 10
Данные считаются свежими 10 минут.
Подходит для:
staleTime: Infinity
Данные никогда не становятся stale автоматически.
Повторное обновление возможно только:
Одним из самых полезных механизмов является
refetchOnWindowFocus.
Когда пользователь возвращается во вкладку браузера, TanStack Query может автоматически обновить данные.
useQuery({
queryKey: ['notifications'],
queryFn: fetchNotifications,
refetchOnWindowFocus: true
})
Механизм работает так:
Это особенно полезно для:
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
refetchOnWindowFocus: false
})
Полезно, если:
Важно понимать взаимодействие со staleTime.
useQuery({
queryKey: ['profile'],
queryFn: fetchProfile,
staleTime: 1000 * 60 * 5,
refetchOnWindowFocus: true
})
Если пользователь вернулся во вкладку через:
Потому что refetch выполняется только для stale-запросов.
Этот параметр определяет поведение при повторном mount компонента.
useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
refetchOnMount: true
})
Когда компонент монтируется повторно:
Поведение по умолчанию.
Refetch выполняется только если данные stale.
refetchOnMount: false
Повторный mount не вызывает refetch.
refetchOnMount: 'always'
Запрос выполняется всегда при mount.
Даже если данные fresh.
Полезно для:
TanStack Query отслеживает состояние сети.
После восстановления соединения библиотека может автоматически обновить данные.
useQuery({
queryKey: ['messages'],
queryFn: fetchMessages,
refetchOnReconnect: true
})
Сценарий работы:
useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
refetchOnReconnect: false
})
Иногда полезно для:
TanStack Query поддерживает polling через
refetchInterval.
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
refetchInterval: 5000
})
Запрос будет повторяться каждые 5 секунд.
При активном query:
refetchInterval: 5000
TanStack Query:
По умолчанию polling останавливается в неактивной вкладке браузера.
Это экономит:
Для продолжения polling в фоне используется:
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
refetchInterval: 5000,
refetchIntervalInBackground: true
})
Теперь refetch продолжится даже при свернутом окне.
Интервал может вычисляться динамически.
useQuery({
queryKey: ['job'],
queryFn: fetchJob,
refetchInterval: (data) => {
if (data?.status === 'finished') {
return false
}
return 2000
}
})
Пока задача выполняется:
После завершения:
refetchInterval: false
Polling отключён.
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
enabled: isActive,
refetchInterval: 3000
})
При enabled: false:
Автоматическое обновление можно комбинировать с ручным.
const { data, refetch } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers
})
Вызов:
await refetch()
принудительно выполняет запрос.
Одним из важнейших механизмов обновления является invalidation.
queryClient.invalidateQueries({
queryKey: ['users']
})
Invalidate:
Типичный сценарий:
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['posts']
})
}
})
После создания записи:
По умолчанию refetch выполняется только для active queries.
Неактивные query:
queryClient.refetchQueries({
queryKey: ['users'],
type: 'inactive'
})
или:
queryClient.refetchQueries({
queryKey: ['users'],
type: 'all'
})
TanStack Query предотвращает дублирование запросов.
Если одновременно несколько компонентов используют:
['users']
будет выполнен только один HTTP-запрос.
Остальные компоненты подпишутся на тот же Promise.
Допустим:
<UserList />
<UserSidebar />
<UserStats />
Все используют:
queryKey: ['users']
TanStack Query:
Это особенно важно при автоматическом refetch.
Во время автоматического обновления старые данные остаются доступными.
Интерфейс не очищается.
const {
data,
isFetching
} = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts
})
Во время background refetch:
data содержит старые данные;isFetching === true.isLoading
Истинен только при первой загрузке.
isFetching
Истинен при любом запросе:
Пример:
function Posts() {
const {
data,
isFetching
} = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
refetchInterval: 10000
})
return (
<div>
{isFetching && (
<span>Обновление...</span>
)}
{data.map(post => (
<div key={post.id}>
{post.title}
</div>
))}
</div>
)
}
Интерфейс остаётся стабильным даже во время обновления.
TanStack Query поддерживает различные режимы работы сети.
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
networkMode: 'online'
})
Стандартное поведение.
Запросы зависят от состояния сети.
networkMode: 'always'
Запросы выполняются даже при offline status.
Полезно для:
networkMode: 'offlineFirst'
Оптимизировано для offline-capable приложений.
Внутренний менеджер фокуса управляет:
Можно переопределить поведение.
import { focusManager } from '@tanstack/react-query'
focusManager.setEventListener((handleFocus) => {
window.addEventListener('visibilitychange', handleFocus)
return () => {
window.removeEventListener(
'visibilitychange',
handleFocus
)
}
})
Менеджер сети управляет reconnect logic.
import { onlineManager } from '@tanstack/react-query'
onlineManager.setEventListener((setOnline) => {
window.addEventListener('online', () => {
setOnline(true)
})
window.addEventListener('offline', () => {
setOnline(false)
})
return () => {
window.removeEventListener('online', setOnline)
window.removeEventListener('offline', setOnline)
}
})
Настройки можно вынести в QueryClient.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60,
refetchOnWindowFocus: false,
refetchOnReconnect: true,
refetchOnMount: true
}
}
})
Теперь все query наследуют эти параметры.
staleTime: 1000 * 60 * 30
staleTime: 1000 * 60 * 5
refetchInterval: 1000
refetchOnWindowFocus: true
refetchOnReconnect: true
Слишком агрессивное обновление может привести к:
Пример плохой конфигурации:
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
staleTime: 0,
refetchInterval: 500
})
Каждые полсекунды будет выполняться новый запрос.
useQuery({
queryKey: ['stats'],
queryFn: fetchStats,
staleTime: 5000,
refetchInterval: 10000,
refetchOnWindowFocus: false
})
Такой подход уменьшает количество сетевых операций и сохраняет актуальность данных.
На практике механизмы обычно работают вместе.
useQuery({
queryKey: ['dashboard'],
queryFn: fetchDashboard,
staleTime: 1000 * 30,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchInterval: 60000
})
Поведение:
Такой подход позволяет поддерживать интерфейс синхронизированным с сервером практически без ручного управления состоянием.