スキル一覧に戻る
keidsondesigner

create-react-query-hook

by keidsondesigner

0🍴 0📅 2026年1月16日
GitHubで見るManusで実行

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

  1. Identificar tipo: Query (buscar) ou Mutation (modificar)
  2. Criar arquivo: Em /queries ou /mutations
  3. Exportar key function: Com parâmetros se necessário
  4. Criar hook: useQuery ou useMutation
  5. Adicionar invalidação: onSuccess para mutations
  6. 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 const no 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 const no mutation key

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です