TanStack Query (React Query)
Domine cache, mutations, otimização e boas práticas para gerenciar estado assíncrono de forma profissional.
Este guia assume conhecimento básico de React e hooks. Foco em uso prático e real.
Configuração básica
Instale o React Query e configure o QueryClient no root da sua aplicação.
npm install @tanstack/react-query# oupnpm add @tanstack/react-query
'use client';import { QueryClient, QueryClientProvider } from '@tanstack/react-query';import { ReactQueryDevtools } from '@tanstack/react-query-devtools';import { useState } from 'react';export default function RootLayout({ children }) {const [queryClient] = useState(() => new QueryClient({defaultOptions: {queries: {staleTime: 60 * 1000, // 1 minutoretry: 1,},},}));return (<QueryClientProvider client={queryClient}>{children}<ReactQueryDevtools initialIsOpen={false} /></QueryClientProvider>);}
GET requests
Dados são cacheados e reutilizados automaticamente
Atualiza dados quando necessário (window focus)
Estados de loading, error e success prontos
Tenta novamente em caso de falha
import { useQuery } from '@tanstack/react-query';async function fetchUser(id: string) {const res = await fetch(`/api/users/${id}`);if (!res.ok) throw new Error('Failed to fetch');return res.json();}export function UserProfile({ userId }: { userId: string }) {const { data, isLoading, error } = useQuery({queryKey: ['user', userId],queryFn: () => fetchUser(userId),});if (isLoading) return <div>Carregando...</div>;if (error) return <div>Erro: {error.message}</div>;return (<div><h1>{data.name}</h1><p>{data.email}</p></div>);}
POST, PUT, DELETE
Use mutations para criar, atualizar ou deletar dados. Invalide o cache automaticamente após sucesso.
import { useMutation, useQueryClient } from '@tanstack/react-query';async function createUser(data: { name: string; email: string }) {const res = await fetch('/api/users', {method: 'POST',body: JSON.stringify(data),});if (!res.ok) throw new Error('Failed to create');return res.json();}export function CreateUserForm() {const queryClient = useQueryClient();const mutation = useMutation({mutationFn: createUser,onSuccess: () => {// Invalida e refetch da lista de usuáriosqueryClient.invalidateQueries({ queryKey: ['users'] });},});const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {e.preventDefault();const formData = new FormData(e.currentTarget);mutation.mutate({name: formData.get('name') as string,email: formData.get('email') as string,});};return (<form onSubmit={handleSubmit}><input name="name" required /><input name="email" type="email" required /><button disabled={mutation.isPending}>{mutation.isPending ? 'Criando...' : 'Criar'}</button>{mutation.isError && <p>Erro: {mutation.error.message}</p>}</form>);}
Dicas essenciais
Sempre use arrays: ['users', userId] ao invés de strings simples
Defina quanto tempo os dados são considerados 'frescos'
Use invalidateQueries com queryKey específica, não tudo
Atualize UI antes da resposta do servidor para melhor UX
Sempre trate erros com error boundaries ou try/catch
Documentação e ferramentas
Explore mais guias e ferramentas práticas para devs.