Skip to main content

frontend

Use when designing React component structure, deciding where state should live, implementing data fetching with TanStack Query, building validated forms, or optimizing rendering performance.

Informações da origem

Repositório
kid-sid/codex-spellbook
Última atividade na origem
11 de maio de 2026 às 18:04
Idioma detectado do SKILL.md
inglês
Estrelas
22
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
frontend
description
Use when designing React component structure, deciding where state should live, implementing data fetching with TanStack Query, building validated forms, or optimizing rendering performance.
# Frontend Patterns Conventions and best practices for building maintainable, performant React applications. ## When to Activate - Designing component structure or deciding where state should live - Choosing between local state, Context, Zustand, or Redux Toolkit - Implementing data fetching, caching, or server state synchronization - Building forms with validation and submission handling - Optimizing rendering performance (re-renders, bundle size, lazy loading) - Setting up routing with protected routes or nested layouts - Writing tests for React components and hooks ## Component Design ### Single Responsibility ```tsx // BAD: component does too many things function UserDashboard({ userId }: { userId: string }) { const [user, setUser] = useState(null); const [orders, setOrders] = useState([]); useEffect(() => { /* fetch user + orders */ }, []); return <div>{/* render user, orders, sidebar, notifications */}</div>; } // GOOD: split by concern function UserDashboard({ userId }: { userId: string }) { return ( <DashboardLayout> <UserProfile userId={userId} /> <OrderList userId={userId} /> <NotificationPanel userId={userId} /> </DashboardLayout> ); } ``` ### Composition over Props Drilling ```tsx // BAD: prop drilling through 3+ levels <Page user={user} onLogout={onLogout} theme={theme} /> <Header user={user} onLogout={onLogout} theme={theme} /> <Nav user={user} onLogout={onLogout} theme={theme} /> // GOOD: compound component or slot pattern <Page> <Header> <Nav /> <UserMenu /> </Header> <main>{children}</main> </Page> ``` ### Container / Presentational Split ```tsx // Container: owns data fetching and state function OrderListContainer({ userId }: { userId: string }) { const { data, isLoading, error } = useOrders(userId); if (isLoading) return <OrderListSkeleton />; if (error) return <ErrorBoundary error={error} />; return <OrderList orders={data} />; } // Presentational: pure rendering, easily testable function OrderList({ orders }: { orders: Order[] }) { return ( <ul> {orders.map(o => <OrderItem key={o.id} order={o} />)} </ul> ); } ``` ## State Management ### Decision Matrix | State Type | Tool | Examples | |---|---|---| | UI / ephemeral | `useState` / `useReducer` | modal open, input value, toggle | | Shared UI state | Zustand or Context | theme, sidebar collapsed, selected tab | | Server state | TanStack Query | API responses, lists, detail pages | | Complex client state | Redux Toolkit | shopping cart, multi-step wizard, offline queue | | URL state | Search params | filters, pagination, selected item | | Form state | React Hook Form | field values, validation, submission | ### Zustand (preferred for shared UI state) ```tsx import { create } from "zustand"; interface SidebarStore { isOpen: boolean; toggle: () => void; open: () => void; close: () => void; } const useSidebarStore = create<SidebarStore>((set) => ({ isOpen: false, toggle: () => set((s) => ({ isOpen: !s.isOpen })), open: () => set({ isOpen: true }), close: () => set({ isOpen: false }), })); // With persistence import { persist } from "zustand/middleware"; const useThemeStore = create(persist( (set) => ({ theme: "light", setTheme: (t) => set({ theme: t }) }), { name: "theme-storage" }, )); ``` ### Redux Toolkit (complex client state) ```tsx import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; const fetchOrders = createAsyncThunk("orders/fetch", async (userId: string) => { const res = await fetch(`/api/users/${userId}/orders`); return res.json(); }); const ordersSlice = createSlice({ name: "orders", initialState: { items: [] as Order[], status: "idle" as LoadingStatus }, reducers: { removeOrder: (state, action) => { state.items = state.items.filter(o => o.id !== action.payload); }, }, extraReducers: (builder) => { builder .addCase(fetchOrders.pending, (state) => { state.status = "loading"; }) .addCase(fetchOrders.fulfilled, (state, action) => { state.status = "succeeded"; state.items = action.payload; }) .addCase(fetchOrders.rejected, (state) => { state.status = "failed"; }); }, }); ``` ## Data Fetching ### TanStack Query (server state) ```tsx import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; // Fetching function useOrders(userId: string) { return useQuery({ queryKey: ["orders", userId], queryFn: () => fetchOrders(userId), staleTime: 5 * 60 * 1000, // 5 min — don't refetch if fresh gcTime: 10 * 60 * 1000, // 10 min — keep in cache after unmount }); } // Mutation with optimistic update function useCancelOrder() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (orderId: string) => cancelOrder(orderId), onMutate: async (orderId) => { await queryClient.cancelQueries({ queryKey: ["orders"] }); const prev = queryClient.getQueryData(["orders"]); queryClient.setQueryData(["orders"], (old: Order[]) => old.filter(o => o.id !== orderId), ); return { prev }; }, onError: (_, __, ctx) => { queryClient.setQueryData(["orders"], ctx?.prev); }, onSettled: () => queryClient.invalidateQueries({ queryKey: ["orders"] }), }); } ``` ### Query Key Conventions ```tsx // Hierarchical keys enable targeted invalidation ["users"] // all users ["users", userId] // one user ["users", userId, "orders"] // user's orders ["users", userId, "orders", { status: "active" }] // filtered queryClient.invalidateQueries({ queryKey: ["users", userId] }); // ^ invalidates user + all sub-keys (orders, profile, etc.) ``` ## Forms ### React Hook Form + Zod ```tsx import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; const schema = z.object({ email: z.string().email("Invalid email"), password: z.string().min(8, "At least 8 characters"), role: z.enum(["admin", "user"]), }); type FormValues = z.infer<typeof schema>; function CreateUserForm() { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormValues>({ resolver: zodResolver(schema) }); const onSubmit = async (data: FormValues) => { await createUser(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("email")} /> {errors.email && <p>{errors.email.message}</p>} <input type="password" {...register("password")} /> {errors.password && <p>{errors.password.message}</p>} <button type="submit" disabled={isSubmitting}> {isSubmitting ? "Creating…" : "Create User"} </button> </form> ); } ``` ## Routing (React Router v6) ### Nested Layouts with Protected Routes ```tsx import { createBrowserRouter, RouterProvider, Outlet, Navigate } from "react-router-dom"; function RequireAuth() { const { user } = useAuth(); return user ? <Outlet /> : <Navigate to="/login" replace />; } const router = createBrowserRouter([ { path: "/login", element: <LoginPage /> }, { element: <RequireAuth />, children: [ { element: <AppShell />, // persistent layout (nav, sidebar) children: [ { path: "/", element: <Dashboard /> }, { path: "/orders", element: <OrderList /> }, { path: "/orders/:id", element: <OrderDetail />, loader: ({ params }) => fetchOrder(params.id!) }, ], }, ], }, ]); ``` ### URL State for Filters ```tsx import { useSearchParams } from "react-router-dom"; function OrderFilters() { const [params, setParams] = useSearchParams(); const status = params.get("status") ?? "all"; return ( <select value={status} onChange={e => setParams(p => { p.set("status", e.target.value); return p; }) }> <option value="all">All</option> <option value="active">Active</option> </select> ); } ``` ## Performance ### Memoization — when it helps ```tsx // useMemo: expensive derivation, stable reference for children const sortedOrders = useMemo( () => [...orders].sort((a, b) => b.total - a.total), [orders], ); // useCallback: stable reference passed to memo'd child or as dep const handleCancel = useCallback((id: string) => cancelOrder(id), [cancelOrder]); // memo: skip re-render when props unchanged const OrderItem = memo(function OrderItem({ order }: { order: Order }) { return <li>{order.id}</li>; }); // BAD: wrapping everything — memo adds overhead, only helps when re-renders are expensive ``` ### Code Splitting ```tsx import { lazy, Suspense } from "react"; // Route-level splitting (most impactful) const Dashboard = lazy(() => import("./pages/Dashboard")); const Reports = lazy(() => import("./pages/Reports")); function App() { return ( <Suspense fallback={<PageSkeleton />}> <Routes> <Route path="/" element={<Dashboard />} /> <Route path="/reports" element={<Reports />} /> </Routes> </Suspense> ); } ``` ### Virtualization for Long Lists ```tsx import { useVirtualizer } from "@tanstack/react-virtual"; function VirtualOrderList({ orders }: { orders: Order[] }) { const parentRef = useRef<HTMLDivElement>(null); const virtualizer = useVirtualizer({ count: orders.length, getScrollElement: () => parentRef.current, estimateSize: () => 64, }); return ( <div ref={parentRef} style={{ height: "600px", overflow: "auto" }}> <div style={{ height: virtualizer.getTotalSize() }}> {virtualizer.getVirtualItems().map(item => (
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub