TanStack Query
Como o QueryClient é configurado e como usar devtools.
Provider
O QueryClient é criado em shared/components/providers/AppProviders.tsx, dentro de um client component,
usando useState — isso evita o bug clássico de instanciar o client no escopo do módulo (o que causaria
cache compartilhado entre requests/usuários diferentes em SSR):
'use client';
export function AppProviders({ children }: AppProvidersProps) {
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
{children}
<Toaster richColors />
{process.env.NODE_ENV === 'development' && <ReactQueryDevtools initialIsOpen={false} />}
</QueryClientProvider>
);
}Defaults
O QueryClient não define defaultOptions — usa os padrões do TanStack Query v5 (staleTime: 0,
refetchOnWindowFocus: true, retry: 3). Se uma tela estiver refazendo requisições com mais frequência do
que o esperado, é por causa disso; ajuste caso a caso via opções do hook (useApiQuery) antes de mudar o
default global.
Devtools
O botão flutuante do React Query Devtools aparece só em desenvolvimento (NODE_ENV === 'development'),
nunca é incluído no bundle de produção.
Hooks de mutação
A maioria das mutações invalida a lista correspondente no onSuccess:
export const useForceDeleteCandidate = () => {
const queryClient = useQueryClient();
return useApiMutation<void, string>({
mutationFn: forceDeleteCandidate,
onSuccess: () => queryClient.invalidateQueries({ queryKey: candidateQueryKeys.lists() }),
});
};Evite queryClient.invalidateQueries() sem argumentos (invalida o cache inteiro) a menos que seja realmente
essa a intenção — como no login, onde tudo pode ter mudado.
Invalidação mínima esperada
Quando a mutação altera uma entidade que também aparece em telas relacionadas, invalide todos os caches afetados pelo contrato, não só a tela atual. Exemplos:
- Candidatura criada/removida/aprovada/desistida: invalidar
applicationKeys.lists(), o detalhe da vaga (vacancyQueryKeys.detail(vacancyId)), listas de vagas e listas/detalhe do candidato. - Vaga aprovada/rejeitada/fechada/reaberta/editada: invalidar listas de vagas e
vacancyQueryKeys.detail(vacancyId). - Candidato atualizado, especialmente restrição: invalidar listas de candidatos e
candidateQueryKeys.detail(candidateId).
Esse padrão evita telas que só mostram o estado correto depois de atualizar com F5.