Dev Highlights

Доки по разработке

This project is maintained by teniryte

7. Инвалидация и фоновые обновления

Инвалидация сообщает TanStack Query, что данные больше нельзя считать актуальными. Это основной механизм синхронизации query cache после мутаций, сокет-событий и внешних изменений.

invalidateQueries

await queryClient.invalidateQueries({
  queryKey: ['todos'],
})

Что происходит:

Тонкая настройка

await queryClient.invalidateQueries({
  queryKey: ['todos'],
  exact: false,
  refetchType: 'active',
})

refetchType:

refetchQueries

Если вы хотите именно принудительный рефетч, а не просто маркировку stale:

await queryClient.refetchQueries({
  queryKey: ['todos'],
  type: 'active',
})

На практике чаще нужен invalidateQueries, а не refetchQueries.

cancelQueries

Перед optimistic update почти всегда сначала:

await queryClient.cancelQueries({ queryKey: ['todos'] })

Зачем:

setQueryData и внешние события

Если данные пришли из WebSocket, SSE или BroadcastChannel:

queryClient.setQueryData(['todos', 'detail', todo.id], todo)

Это лучше, чем немедленно дёргать сеть ещё раз.

Частый production-паттерн:

Автоматические фоновые триггеры

Stale-запросы по умолчанию рефетчатся:

Это одна из самых важных “магий” библиотеки. Если команда не знает об этих триггерах, поведение может казаться случайным.

staleTime как главный рычаг

useQuery({
  queryKey: ['feed'],
  queryFn: fetchFeed,
  staleTime: 2 * 60_000,
})

Чем больше staleTime, тем меньше автоматических рефетчей.

Вместо рефлекса “отключить всё” чаще достаточно просто осмысленно поднять staleTime.

Поллинг

useQuery({
  queryKey: ['build-status', buildId],
  queryFn: () => fetchBuildStatus(buildId),
  refetchInterval: (query) =>
    query.state.data?.finished ? false : 5_000,
})

Это удобнее, чем ручной setInterval.

refetchOnWindowFocus, refetchOnMount, refetchOnReconnect

Эти флаги не заменяют staleTime, а дополняют его.

Типовые настройки:

useQuery({
  queryKey: ['profile'],
  queryFn: fetchProfile,
  staleTime: 5 * 60_000,
  refetchOnWindowFocus: false,
})

Подходит, если вкладка часто теряет фокус, а данные не критичны.

Инвалидация после мутации

useMutation({
  mutationFn: updateTodo,
  onSuccess: (_data, variables) => {
    return queryClient.invalidateQueries({
      queryKey: ['todos', 'detail', variables.id],
    })
  },
})

Если затронуты и detail, и list:

await Promise.all([
  queryClient.invalidateQueries({ queryKey: ['todos', 'detail', id] }),
  queryClient.invalidateQueries({ queryKey: ['todos', 'list'] }),
])

removeQueries, resetQueries, clear

Это не одно и то же:

Типичный случай для clear: logout с полной сменой пользователя.

Практические рекомендации