08 — React Query: Cache, staleTime e refetch
Pergunta: o tempo de mount sobra de rede ou de decisão de refetch? Onde a cache está sendo subutilizada (refetch desnecessário) e onde está sendo agressiva demais (dados stale demais)?
Contexto
src/routes.tsx:34-35 define o default global:
staleTime: 5 * 60 * 1000 // 5 min
gcTime: 30 * 60 * 1000 // 30 minSignifica: por padrão toda query é considerada fresca por 5 min, sem refetch em remount. Bom default.
Evidência
| Cluster | Hooks | staleTime | Observação |
|---|---|---|---|
| Default global | routes.tsx | 5 min | Aplica em qualquer useQuery sem override |
| Admin Financeiro | useAdminFinanceiro.ts:175..276 | 10s–60s | Coerente (dados de pagamento em movimento) |
| Health/Cron | useAdminHealthCheck.ts:115 | 30s | Coerente |
| Inscrições / Videos / Batch-init | useInscricoesOlimpiada.ts:33, useVideosCoord.ts:105 | 5 min | Redundante com global — verificar se é intencional |
| Realtime-backed | useFeatureLocksRealtime.ts, useFeatureLocks.ts | (sem override) | OK quando realtime invalida cache |
Gap 1 — refetchOnWindowFocus / refetchOnMount
Não há override desses no global. Default do React Query 5 é refetchOnWindowFocus: true e refetchOnMount: 'always'. Combinado com staleTime: 5min o efeito é:
- ✅ Mount com dados frescos não refetch (bom).
- ❌ Mount com dados stale (>5min) refetch — comum quando o coord deixa a aba aberta e volta.
- ❌ Toda volta de foco da janela refetch — em telas pesadas isso é o waterfall percebido.
Gap 2 — keepPreviousData ausente em listas paginadas
Quando virar paginação real no servidor (F1), obrigatório placeholderData: keepPreviousData. Sem isso, a tabela some/skeleton entre páginas e o usuário percebe "lentidão" mesmo com TTFB de 200ms.
Buscar: nenhum hook em src/hooks/ usa keepPreviousData ou placeholderData hoje.
Gap 3 — Invalidação ampla demais
invalidateQueries(['resultados']) invalida todas as variações (todas as escolas, todos os filtros). Em sessões multi-escola isso refaz queries inúteis. SSOT existente (mem://architecture/query-key-context-isolation) exige escola_id na chave — verificar adesão em todos os invalidateQueries do Resultados.
Hipóteses a validar
- H1: Refetch em
refetchOnWindowFocusé responsável por boa parte do "ele demorou de novo" que o usuário relata em segunda navegação. - H2: A ausência de
placeholderDatavai tornar a paginação servidor (F1) percebida como pior do que é. - H3: Invalidações estouram cache de outras escolas em sessão multi-perfil.
Próximas etapas (auditoria, não fix)
- Listar todos os
invalidateQueriesde mutação emresultados/*e cruzar com a query-key real. - Decidir no SSOT (99):
refetchOnWindowFocus: falsepor padrão + opt-in nos hooks que precisam (saldo financeiro, presença). - Reservar
placeholderData: keepPreviousDatacomo obrigatório em qualquer hook paginado servidor.
Links
- 02 — Pagination fetchAllRows
- 99 — Regra de qualidade
mem://architecture/query-key-context-isolation