Skip to main content

Cache | TanStack | React

  • Cache | TanStack | React

Cache no TanStack Query no Next.js (App Router)

O TanStack Query implementa cache em memória no lado do cliente. Ele evita requisições repetidas enquanto os dados estiverem considerados válidos (fresh) e enquanto o QueryClient estiver ativo.

Essa documentação descreve:

  • Como o cache é ativado
  • Como ele funciona internamente
  • Quando ele não funciona
  • Configuração correta em Next.js
  • Cenários reais de comportamento

Arquitetura do Cache

O cache depende de três elementos:

  1. QueryClient
  2. QueryClientProvider
  3. useQuery

Fluxo simplificado:

useQuery → verifica cache pelo queryKey
→ se não existir → executa queryFn
→ salva resultado no QueryClient
→ retorna dados para o componente

Se o queryKey já existir e estiver dentro do staleTime, nenhuma nova requisição é feita.


Configuração Base

QueryClient

import { QueryClient } from '@tanstack/react-query'

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
refetchOnWindowFocus: false,
},
},
})

Provider (deve estar no layout raiz)

"use client"

import { QueryClientProvider } from "@tanstack/react-query"
import { queryClient } from "@/lib/queryClient"

export function ReactQueryProvider({ children }) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}

Se o provider desmontar, o cache é destruído.


Parâmetros que Controlam o Cache

staleTime

Define por quanto tempo os dados são considerados frescos.

staleTime: 5 * 60 * 1000

Enquanto estiver fresco:

  • Não ocorre refetch automático
  • Mesmo ao navegar entre rotas

Se for:

staleTime: Infinity

Nunca refaz automaticamente.


gcTime (antigo cacheTime)

Tempo que o cache permanece em memória após não estar sendo usado.

gcTime: 10 * 60 * 1000

Se nenhum componente estiver usando a query e o tempo expirar, o cache é removido.


refetchOnWindowFocus

Controla se refaz a requisição ao voltar para a aba.

refetchOnWindowFocus: false

refetchOnMount

Controla se refaz ao montar o componente.

refetchOnMount: false

refetchOnReconnect

Refaz ao reconectar à internet.

refetchOnReconnect: true

Quando o Cache Funciona

O cache funciona quando:

  • Navegação ocorre via <Link /> ou router.push
  • O QueryClientProvider permanece montado
  • A página não foi recarregada
  • O tempo de staleTime ainda não expirou

Exemplo:

  1. Acessa /products
  2. Dados carregam
  3. Vai para /
  4. Volta para /products dentro de 5 minutos

Resultado:

→ Nenhuma nova requisição é feita


Quando o Cache Não Funciona

1. Hard Reload (F5)

Ao recarregar a página:

  • React reinicia
  • QueryClient é recriado
  • Cache em memória é perdido

2. Digitar URL manualmente + Enter

Isso provoca reload completo do navegador.

Mesmo comportamento do F5.


3. Provider fora do layout raiz

Se o QueryClientProvider estiver:

  • Dentro da página
  • Em um layout intermediário

Ao navegar, ele desmonta e o cache é perdido.

Estrutura correta:

app/layout.tsx
└── ReactQueryProvider
└── children

4. QueryClient sendo recriado

Errado:

function Provider() {
const queryClient = new QueryClient()
}

Correto:

const queryClient = new QueryClient()

5. Strict Mode em desenvolvimento

No Next.js:

reactStrictMode: true

Em ambiente DEV:

  • O componente monta duas vezes
  • A query pode executar duas vezes

Isso não acontece em produção.

Teste real:

npm run build
npm start

Comportamento Visual Simplificado

Primeira vez na página

Cache vazio → Executa query → Armazena resultado

Voltar dentro do staleTime

Cache válido → Retorna do cache → Sem request

Após staleTime expirar

Cache stale → Executa nova request

Estratégia de Cache Estável

Configuração para evitar qualquer refetch automático:

useQuery({
queryKey: ['products'],
queryFn: getProductsAsync,
staleTime: Infinity,
gcTime: Infinity,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
})

Nesse cenário, só haverá nova requisição se:

queryClient.invalidateQueries({ queryKey: ['products'] })

Persistência de Cache após Reload

Por padrão, o cache é apenas em memória.

Para manter após F5:

npm install @tanstack/react-query-persist-client

Exemplo com localStorage:

import { persistQueryClient } from '@tanstack/react-query-persist-client'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'

const persister = createSyncStoragePersister({
storage: window.localStorage,
})

persistQueryClient({
queryClient,
persister,
})

Agora o cache sobrevive a reload.


Diagnóstico Prático

Se estiver vendo requisição na aba Network:

  1. Verifique se é a primeira execução
  2. Teste navegação via Link
  3. Teste sem dar F5
  4. Confirme se o Provider está no layout raiz
  5. Teste build de produção

Resumo Técnico

O cache do TanStack Query:

  • É client-side
  • É em memória
  • Depende do lifecycle do React
  • Não sobrevive a reload por padrão
  • Funciona corretamente com navegação SPA