
create-react-query-hook
by keidsondesigner
SKILL.md
name: create-react-query-hook description: Cria hooks React Query (queries e mutations) seguindo os padrões Bewear com invalidação de cache e integração com Server Actions. tools: Read, Write, Edit, Glob, Grep
Create React Query Hook Skill
Esta skill cria hooks React Query para gerenciamento de estado do servidor, seguindo os padrões do projeto Bewear.
Quando Usar
- Criar hooks de query (buscar dados)
- Criar hooks de mutation (modificar dados)
- Integrar Server Actions com React Query
- Implementar cache invalidation
Regras Obrigatórias
1. Estrutura de Arquivos
src/hooks/
├── queries/
│ ├── use-cart.ts
│ ├── use-shipping-addresses.ts
│ └── ...
└── mutations/
├── use-increase-product-quantity-cart.ts
├── use-create-shipping-address.ts
└── ...
Nomenclatura:
- Queries:
use-nome-recurso.ts(plural se lista) - Mutations:
use-verbo-recurso.ts(ação específica)
2. Query Hook Template
Arquivo de referência: src/hooks/queries/use-cart.ts
import { getNome } from "@/actions/get-nome";
import { useQuery } from "@tanstack/react-query";
// 1. SEMPRE exportar função de query key
export const getUseNomeQueryKey = () => ["nome"] as const;
// 2. Hook de query
export const useNome = () => {
return useQuery({
queryKey: getUseNomeQueryKey(),
queryFn: () => getNome(),
});
};
Uso no componente:
const { data, isLoading, error } = useNome();
if (isLoading) return <p>Carregando...</p>;
if (error) return <p>Erro: {error.message}</p>;
return <div>{data?.field}</div>;
3. Mutation Hook Template
Arquivo de referência: src/hooks/mutations/use-increase-product-quantity-cart.ts
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { nomeAction } from "@/actions/nome-action";
import { getUseRelatedQueryKey } from "../queries/use-related";
// 1. SEMPRE exportar função de mutation key (com parâmetros se necessário)
export const getNomeMutationKey = (id: string) => {
return ["mutation-nome", id] as const;
};
// 2. Hook de mutation
export const useNomeMutation = (id: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: getNomeMutationKey(id),
mutationFn: (data: SchemaType) => nomeAction(data),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getUseRelatedQueryKey()
});
},
});
};
Uso no componente:
const mutation = useNomeMutation(id);
const handleSubmit = async (data) => {
try {
await mutation.mutateAsync(data);
toast.success("Sucesso!");
} catch (error) {
toast.error("Erro ao processar");
}
};
return (
<Button
onClick={handleSubmit}
disabled={mutation.isPending}
>
{mutation.isPending ? "Processando..." : "Enviar"}
</Button>
);
4. Query Keys Pattern
SEMPRE exporte função de query key:
// Query sem parâmetros
export const getUseCartQueryKey = () => ["cart"] as const;
// Query com parâmetros
export const getUseProductQueryKey = (id: string) => ["product", id] as const;
// Query com múltiplos parâmetros
export const getUseOrdersQueryKey = (userId: string, status?: string) =>
["orders", userId, status].filter(Boolean) as const;
Por que usar as const?
- TypeScript infere array literal readonly
- Melhor type safety
- Padrão recomendado React Query
5. Mutation Keys Pattern
SEMPRE exporte função de mutation key com parâmetros:
// Mutation simples
export const getCreateNomeMutationKey = () => ["create-nome"] as const;
// Mutation com ID
export const getUpdateNomeMutationKey = (id: string) =>
["update-nome", id] as const;
// Mutation com múltiplos parâmetros
export const getAddToCartMutationKey = (productId: string, cartId: string) =>
["add-to-cart", productId, cartId] as const;
6. Cache Invalidation
onSuccess callback para invalidar queries relacionadas:
export const useCreateAddressMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: getCreateAddressMutationKey(),
mutationFn: (data) => createAddress(data),
onSuccess: () => {
// Invalidar lista de endereços
queryClient.invalidateQueries({
queryKey: getUseAddressesQueryKey()
});
// Invalidar carrinho (se endereço foi vinculado)
queryClient.invalidateQueries({
queryKey: getUseCartQueryKey()
});
},
});
};
Referência: src/hooks/mutations/use-increase-product-quantity-cart.ts (linha 16)
7. Query Options
export const useNome = (options?: UseQueryOptions) => {
return useQuery({
queryKey: getUseNomeQueryKey(),
queryFn: () => getNome(),
enabled: true,
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
refetchOnWindowFocus: true,
retry: 3,
...options,
});
};
8. Mutation com Optimistic Updates
export const useUpdateQuantityMutation = (productId: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: getUpdateQuantityMutationKey(productId),
mutationFn: (quantity: number) => updateQuantity(productId, quantity),
onMutate: async (newQuantity) => {
await queryClient.cancelQueries({
queryKey: getUseCartQueryKey()
});
const previousCart = queryClient.getQueryData(getUseCartQueryKey());
queryClient.setQueryData(getUseCartQueryKey(), (old) => ({
...old,
items: old.items.map(item =>
item.productId === productId
? { ...item, quantity: newQuantity }
: item
),
}));
return { previousCart };
},
onError: (err, newQuantity, context) => {
queryClient.setQueryData(
getUseCartQueryKey(),
context?.previousCart
);
},
onSettled: () => {
queryClient.invalidateQueries({
queryKey: getUseCartQueryKey()
});
},
});
};
9. Dependent Queries
export const useOrderDetails = (orderId?: string) => {
return useQuery({
queryKey: getUseOrderDetailsQueryKey(orderId!),
queryFn: () => getOrderDetails(orderId!),
enabled: !!orderId,
});
};
10. Parallel Queries
const { data: cart } = useCart();
const { data: addresses } = useShippingAddresses();
const { data: user } = useUser();
11. Prefetching
import { useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
const handleMouseEnter = () => {
queryClient.prefetchQuery({
queryKey: getUseProductQueryKey(productId),
queryFn: () => getProduct(productId),
});
};
Integração com Server Actions
Server Action Simples (sem schema)
// src/actions/get-cart/index.ts
"use server";
export async function getCart() {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session?.user) {
throw new Error("Unauthorized");
}
return await db.query.cartTable.findFirst({
where: eq(cartTable.userId, session.user.id),
});
}
// src/hooks/queries/use-cart.ts
import { getCart } from "@/actions/get-cart";
import { useQuery } from "@tanstack/react-query";
export const getUseCartQueryKey = () => ["cart"] as const;
export const useCart = () => {
return useQuery({
queryKey: getUseCartQueryKey(),
queryFn: () => getCart(),
});
};
Server Action com Schema
// src/hooks/mutations/use-create-address.ts
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { createAddress } from "@/actions/create-address";
import { CreateAddressSchema } from "@/actions/create-address/schema";
import { getUseAddressesQueryKey } from "../queries/use-addresses";
export const getCreateAddressMutationKey = () => ["create-address"] as const;
export const useCreateAddressMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: getCreateAddressMutationKey(),
mutationFn: (data: CreateAddressSchema) => createAddress(data),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getUseAddressesQueryKey()
});
},
});
};
Estados do Hook
Query States
const {
data,
isLoading,
isFetching,
isError,
error,
isSuccess,
refetch,
} = useNome();
Mutation States
const {
mutate,
mutateAsync,
isPending,
isError,
error,
isSuccess,
data,
reset,
} = useNomeMutation();
Provider Configuration
Já configurado em: src/providers/react-query.tsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function ReactQueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
}));
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
Arquivos de Referência
- Query Hook:
src/hooks/queries/use-cart.ts - Mutation Hook:
src/hooks/mutations/use-increase-product-quantity-cart.ts - Mutation com Schema:
src/hooks/mutations/use-create-shipping-address.ts - Provider:
src/providers/react-query.tsx - Uso em componente:
src/app/cart/components/addresses.tsx
Processo de Criação
- Identificar tipo: Query (buscar) ou Mutation (modificar)
- Criar arquivo: Em
/queriesou/mutations - Exportar key function: Com parâmetros se necessário
- Criar hook: useQuery ou useMutation
- Adicionar invalidação: onSuccess para mutations
- Testar estados: Loading, error, success
Checklist
Query Hook
- Arquivo em
src/hooks/queries/ - Exportar função de query key
- useQuery com queryKey e queryFn
- Integrar com Server Action
- Usar
as constno query key
Mutation Hook
- Arquivo em
src/hooks/mutations/ - Exportar função de mutation key
- useMutation com mutationKey e mutationFn
- useQueryClient importado
- onSuccess com invalidateQueries
- Invalidar queries relacionadas
- Usar
as constno mutation key
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です