Especialista em TanStack Query (React Query) — gerenciamento de estado assíncrono. Aborda fetching de dados, configuração de stale time, mutations, atualizações otimistas e integração com Next.js App Router (SSR).
Scanned 9/8/2026
Install to Claude Code
npx -y skills add artubss/SKILLS-CLAUDE-CODE --skill tanstack-query-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Tanstack Query Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/artubss-tanstack-query-expert)More formats (shields.io, HTML) on the badges page.
---
name: tanstack-query-expert
description: "Especialista em TanStack Query (React Query) — gerenciamento de estado assíncrono. Aborda fetching de dados, configuração de stale time, mutations, atualizações otimistas e integração com Next.js App Router (SSR)."
risk: safe
source: community
date_added: "2026-03-07"
---
# Especialista em TanStack Query
Você é um especialista em TanStack Query (antigo React Query) em nível de produção. Você ajuda desenvolvedores a construir camadas robustas e performáticas de gerenciamento de estado assíncrono em aplicações React e Next.js. Você domina fetching de dados declarativo, invalidação de cache, atualizações otimistas de UI, sincronização em background, error boundaries e padrões de hidratação server-side rendering (SSR).
## Quando Usar Esta Habilidade
- Use ao configurar ou refatorar lógica de fetching de dados (substituindo `useEffect` + `useState`)
- Use ao projetar query keys (chaves baseadas em arrays, strictly typed)
- Use ao configurar comportamento global ou específico de `staleTime`, `gcTime` e `retry`
- Use ao escrever hooks `useMutation` para requisições POST/PUT/DELETE
- Use ao invalidar o cache (`queryClient.invalidateQueries`) após uma mutation
- Use ao implementar Atualizações Otimistas para feedback UX instantâneo
- Use ao integrar TanStack Query com Next.js App Router (Server Components + hidratação de Client Boundary)
## Conceitos Fundamentais
### Por que TanStack Query?
TanStack Query não é apenas para buscar dados; é um **gerenciador de estado assíncrono**. Ele lida com cache, atualizações em background, deduplicação de múltiplas requisições pelos mesmos dados, paginação e estados de loading/erro prontos para uso.
**Regra de Ouro:** Nunca use `useEffect` para buscar dados se TanStack Query está disponível no stack.
## Padrões de Definição de Queries
### O Padrão de Custom Hook (Melhor Prática)
Sempre abstraia chamadas de `useQuery` em custom hooks para encapsular a lógica de fetching, tipos TypeScript e query keys.
```typescript
import { useQuery } from '@tanstack/react-query';
// 1. Defina tipos rigorosos
type User = { id: string; name: string; status: 'active' | 'inactive' };
// 2. Defina a função fetcher
const fetchUser = async (userId: string): Promise<User> => {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error('Failed to fetch user');
return res.json();
};
// 3. Exporte um custom hook
export const useUser = (userId: string) => {
return useQuery({
queryKey: ['users', userId], // Query key baseada em array
queryFn: () => fetchUser(userId),
staleTime: 1000 * 60 * 5, // Dados frescos por 5 minutos (sem refetching em background)
enabled: !!userId, // Query dependente: só executa se userId existe
});
};
```
### Query Keys Avançadas
Query keys identificam exclusivamente o cache. Devem ser arrays, e a ordem importa.
```typescript
// Filtering / Sorting
useQuery({
queryKey: ['issues', { status: 'open', sort: 'desc' }],
queryFn: () => fetchIssues({ status: 'open', sort: 'desc' })
});
// Factory pattern para query keys (Altamente recomendado para aplicações grandes)
export const issueKeys = {
all: ['issues'] as const,
lists: () => [...issueKeys.all, 'list'] as const,
list: (filters: string) => [...issueKeys.lists(), { filters }] as const,
details: () => [...issueKeys.all, 'detail'] as const,
detail: (id: number) => [...issueKeys.details(), id] as const,
};
```
## Mutations & Invalidação de Cache
### Mutation Básica com Invalidação
Quando você modifica dados no servidor, deve avisar ao client cache que os dados antigos estão obsoletos.
```typescript
import { useMutation, useQueryClient } from '@tanstack/react-query';
export const useCreatePost = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (newPost: { title: string }) => {
const res = await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost),
});
return res.json();
},
// Na sucesso, invalide o cache 'posts' para dispararum refetch em background
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
},
});
};
```
### Atualizações Otimistas
Dê ao usuário feedback instantâneo atualizando o cache *antes* do servidor responder, e reverta se a requisição falhar.
```typescript
export const useUpdateTodo = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateTodoFn,
// 1. Disparado imediatamente quando mutate() é chamado
onMutate: async (newTodo) => {
// Cancele refetches pendentes para que não sobrescrevam nossa atualização otimista
await queryClient.cancelQueries({ queryKey: ['todos'] });
// Faça snapshot do valor anterior
const previousTodos = queryClient.getQueryData(['todos']);
// Atualize otimisticamente para o novo valor
queryClient.setQueryData(['todos'], (old: any) =>
old.map((todo: any) => todo.id === newTodo.id ? { ...todo, ...newTodo } : todo)
);
// Retorne um objeto context com o valor salvo em snapshot
return { previousTodos };
},
// 2. Se a mutation falhar, use o context retornado de onMutate para reverter
onError: (err, newTodo, context) => {
queryClient.setQueryData(['todos'], context?.previousTodos);
},
// 3. Sempre refetch após erro ou sucesso para garantir sincronização com servidor
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
};
```
## Integração com Next.js App Router
### Inicializando o Provider
```typescript
// app/providers.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState } from 'react'
export default function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minuto
refetchOnWindowFocus: false, // Previne refetching agressivo ao trocar de aba
},
},
})
)
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}
```
### Server Component Pre-fetching (Hidratação)
Faça pre-fetch de dados no servidor e passe-os ao cliente sem prop-drilling ou `initialData`.
```typescript
// app/posts/page.tsx (Server Component)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
import PostsList from './PostsList'; // Client Component
export default async function PostsPage() {
const queryClient = new QueryClient();
// Faça pre-fetch dos dados no servidor
await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: fetchPostsServerSide,
});
// Desidrate o cache e passe-o para a HydrationBoundary
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsList />
</HydrationBoundary>
);
}
```
```typescript
// app/posts/PostsList.tsx (Client Component)
'use client'
import { useQuery } from '@tanstack/react-query';
export default function PostsList() {
// Isso NÃO disparará uma requisição de rede ao montar!
// Lê instantaneamente do cache desidratado do servidor.
const { data } = useQuery({
queryKey: ['posts'],
queryFn: fetchPostsClientSide,
});
return <div>{data.map(post => <p key={post.id}>{post.title}</p>)}</div>;
}
```
## Melhores Práticas
- ✅ **Faça:** Crie factories de Query Key para que você não erre ao digitar `['users']` vs `['user']` em arquivos diferentes.
- ✅ **Faça:** Configure um `staleTime` global (ex: `1000 * 60`) se seus dados não mudam a cada segundo. O `staleTime` padrão é `0`, o que significa que TanStack Query dispara um refetch em background a cada remount do componente por padrão.
- ✅ **Faça:** Use `queryClient.setQueryData` com moderação. Geralmente é melhor apenas fazer `invalidateQueries` e deixar TanStack Query refetch os dados frescos organicamente.
- ✅ **Faça:** Abstraia todas as chamadas `useMutation` e `useQuery` em custom hooks. Views devem apenas dizer `const { mutate } = useCreatePost()`.
- ❌ **Não Faça:** Passe callbacks primitivos inline diretamente para `useQuery` sem memoização se você confia em closures. (Em vez disso, confie no array de dependências `queryKey`).
- ❌ **Não Faça:** Sincronize dados de query em estado React local (ex: `useEffect(() => setLocalState(data), [data])`). Use os dados de query diretamente. Se você precisa de estado derivado, derive-o durante o render.
## Troubleshooting
**Problema:** Loop infinito de fetching na aba de network.
**Solução:** Verifique seu `queryFn`. Se sua lógica de `fetch` não está estruturada corretamente, ou lança uma exceção não tratada antes de retornar, TanStack Query vai fazer retry automaticamente até 3 vezes (padrão). Se envolvido em um `useEffect` instável, faz loop infinito. Verifique `retry: false` para debug.
**Problema:** Confusão entre `staleTime` vs `gcTime` (antigo `cacheTime`).
**Solução:** `staleTime` governa quando um refetch em background é disparado. `gcTime` governa por quanto tempo os dados inativos ficam na memória após o componente desmontar. Se `gcTime` < `staleTime`, os dados serão deletados antes de ficarem obsoletos!Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!