Implementa gerenciamento de estado assíncrono com React Query (TanStack Query). Use para buscar, cachear, sincronizar e atualizar dados do servidor.
Esta skill orienta a implementação de gerenciamento de dados assíncronos usando @tanstack/react-query v5, seguindo os padrões estabelecidos neste projeto.
Os hooks de React Query devem seguir a estrutura feature-based:
src/features/{feature-name}/
├── actions.ts # Server actions (Next.js)
├── schemas.ts # Tipos e validações Zod
└── hooks/
├── use-{entities}.ts # Query para listar (ex: use-services.ts)
├── use-create-{entity}.ts # Mutation para criar
├── use-update-{entity}.ts # Mutation para atualizar
└── use-delete-{entity}.ts # Mutation para deletar
| Tipo | Padrão | Exemplo |
|---|---|---|
| Query (listar) | use{Entities} | useServices, useUsers |
| Query (detalhe) | use{Entity} | useService, useUser |
| Mutation (criar) | useCreate{Entity} | useCreateService |
| Mutation (atualizar) | useUpdate{Entity} | useUpdateService |
| Mutation (deletar) | useDelete{Entity} | useDeleteService |
Use arrays estruturados para query keys:
// ✅ Bom - permite invalidação granular
queryKey: ["services"]; // Lista todos
queryKey: ["services", { onlyActive: true }]; // Lista com filtro
queryKey: ["services", id]; // Detalhe por ID
// ❌ Evitar - dificulta invalidação
queryKey: ["all-services"];
queryKey: [`service-${id}`];
"use client";
import { useQuery } from "@tanstack/react-query";
import { getEntities } from "../actions";
export function useEntities(filter?: boolean) {
return useQuery({
queryKey: ["entities", { filter }],
queryFn: () => getEntities(filter),
});
}
"use client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { createEntity } from "../actions";
import type { CreateEntityInput } from "../schemas";
export function useCreateEntity() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateEntityInput) => createEntity(data),
onSuccess: (result) => {
if (result.success) {
toast.success("Criado com sucesso!");
queryClient.invalidateQueries({ queryKey: ["entities"] });
} else {
toast.error("Erro ao criar");
}
},
onError: () => {
toast.error("Erro ao criar");
},
});
}
"use client";
import { useEntities } from "../hooks/use-entities";
import { useCreateEntity } from "../hooks/use-create-entity";
export function EntitiesTable() {
const { data, isLoading, error } = useEntities();
const createMutation = useCreateEntity();
if (isLoading) return <Loading />;
if (error) return <Error message={error.message} />;
const handleCreate = (data: CreateEntityInput) => {
createMutation.mutate(data);
};
return <Table data={data} />;
}
[!IMPORTANT] Todos os hooks de React Query devem incluir
"use client"no topo do arquivo.
[!TIP] Sempre invalide queries relacionadas após uma mutation para manter o cache sincronizado.
[!WARNING] Não use
queryClient.setQueryDatapara modificar o cache diretamente, prefirainvalidateQueriespara garantir dados frescos.