- python-developer agent: Django/FastAPI backend specialist - nextjs-patterns skill: App Router, Server Components, Server Actions, Auth.js - vue-nuxt-patterns skill: Composition API, Pinia, Nitro server, SSR - react-patterns skill: hooks, Context, TanStack Query, React Hook Form - python-django-patterns skill: DRF, services, repositories - python-fastapi-patterns skill: async, Pydantic, SQLAlchemy, dependencies - /nextjs pipeline command for full-stack Next.js apps - /vue pipeline command for full-stack Vue/Nuxt apps - Updated frontend-developer with framework-specific skills - Updated orchestrator, capability-index for Python + frontend routing - Updated README, STRUCTURE, EVOLUTION_LOG with all new stacks Total agents: 30. Stacks: PHP, Next.js, Vue/Nuxt, React, Python, Go, Flutter, Node.js
8.3 KiB
8.3 KiB
name, description
| name | description |
|---|---|
| react-patterns | React 18+ patterns — Hooks, composition, context, suspense, concurrent features, component architecture |
React Patterns
Project Structure
src/
├── app/ or pages/ # Next.js or Vite pages
├── components/
│ ├── ui/ # Base UI primitives
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ ├── Modal.tsx
│ │ └── Card.tsx
│ ├── forms/ # Form components
│ │ ├── LoginForm.tsx
│ │ └── ProductForm.tsx
│ └── features/ # Feature components
│ ├── product/
│ ├── cart/
│ └── auth/
├── hooks/ # Custom hooks
│ ├── useAuth.ts
│ ├── useCart.ts
│ ├── useDebounce.ts
│ └── useApi.ts
├── context/ # React Context providers
│ ├── AuthContext.tsx
│ └── CartContext.tsx
├── services/ # API services
│ ├── api.ts # Axios/fetch instance
│ ├── authService.ts
│ └── productService.ts
├── types/ # TypeScript types
│ └── index.ts
├── utils/ # Utilities
│ ├── format.ts
│ └── validation.ts
└── lib/ # Third-party config
├── queryClient.ts # TanStack Query
└── supabase.ts
Component Pattern
// components/product/ProductCard.tsx
import { useState } from 'react';
import { useCart } from '@/hooks/useCart';
interface ProductCardProps {
product: Product;
showAddToCart?: boolean;
onAddToCart?: (productId: string) => void;
}
export function ProductCard({ product, showAddToCart = true, onAddToCart }: ProductCardProps) {
const [isAdding, setIsAdding] = useState(false);
const { addItem } = useCart();
const handleAdd = async () => {
setIsAdding(true);
await addItem(product.id, 1);
onAddToCart?.(product.id);
setIsAdding(false);
};
const formattedPrice = new Intl.NumberFormat('en-US', {
style: 'currency', currency: 'USD',
}).format(product.price);
return (
<div className="border rounded-lg p-4 hover:shadow-lg transition-shadow">
<a href={`/products/${product.id}`}>
<img src={product.image} alt={product.name} className="w-full h-48 object-cover rounded" />
<h3 className="mt-2 font-semibold">{product.name}</h3>
<p className="text-gray-600">{formattedPrice}</p>
</a>
{showAddToCart && (
<button
onClick={handleAdd}
disabled={isAdding}
className="mt-2 w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700 disabled:opacity-50"
>
{isAdding ? 'Adding...' : 'Add to Cart'}
</button>
)}
</div>
);
}
Custom Hook Pattern
// hooks/useCart.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { cartService } from '@/services/cartService';
export function useCart() {
const queryClient = useQueryClient();
const { data: cart } = useQuery({
queryKey: ['cart'],
queryFn: cartService.get,
});
const addItemMutation = useMutation({
mutationFn: cartService.addItem,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['cart'] }),
});
const removeItemMutation = useMutation({
mutationFn: cartService.removeItem,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['cart'] }),
});
const total = cart?.items?.reduce((sum, item) => sum + item.price * item.quantity, 0) ?? 0;
const count = cart?.items?.reduce((sum, item) => sum + item.quantity, 0) ?? 0;
return {
cart,
total,
count,
addItem: addItemMutation.mutateAsync,
removeItem: removeItemMutation.mutateAsync,
isAdding: addItemMutation.isPending,
};
}
Context Pattern
// context/AuthContext.tsx
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
interface AuthContextType {
user: User | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = useCallback(async (email: string, password: string) => {
const response = await authService.login({ email, password });
setUser(response.user);
localStorage.setItem('token', response.token);
}, []);
const logout = useCallback(() => {
setUser(null);
localStorage.removeItem('token');
}, []);
return (
<AuthContext.Provider value={{ user, isAuthenticated: !!user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error('useAuth must be used within AuthProvider');
return context;
}
API Service Pattern
// services/api.ts
import axios from 'axios';
export const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || '/api',
headers: { 'Content-Type': 'application/json' },
});
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// services/productService.ts
import { api } from './api';
export const productService = {
list: (params?: object) => api.get('/products', { params }).then((r) => r.data),
get: (id: string) => api.get(`/products/${id}`).then((r) => r.data),
create: (data: CreateProductDTO) => api.post('/products', data).then((r) => r.data),
update: (id: string, data: UpdateProductDTO) => api.put(`/products/${id}`, data).then((r) => r.data),
delete: (id: string) => api.delete(`/products/${id}`).then((r) => r.data),
};
Form Pattern (React Hook Form + Zod)
// components/forms/ProductForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1, 'Name is required').max(255),
price: z.number().positive('Price must be positive'),
categoryId: z.string().min(1, 'Category is required'),
description: z.string().optional(),
});
type ProductFormData = z.infer<typeof schema>;
export function ProductForm({ onSubmit }: { onSubmit: (data: ProductFormData) => Promise<void> }) {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<ProductFormData>({
resolver: zodResolver(schema),
});
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label>Name</label>
<input {...register('name')} className="border rounded px-3 py-2 w-full" />
{errors.name && <p className="text-red-500 text-sm">{errors.name.message}</p>}
</div>
<div>
<label>Price</label>
<input type="number" step="0.01" {...register('price', { valueAsNumber: true })} className="border rounded px-3 py-2 w-full" />
{errors.price && <p className="text-red-500 text-sm">{errors.price.message}</p>}
</div>
<button type="submit" disabled={isSubmitting} className="bg-blue-600 text-white px-4 py-2 rounded">
{isSubmitting ? 'Saving...' : 'Save Product'}
</button>
</form>
);
}
Checklist
- Functional components with hooks (no class components)
- TypeScript with strict mode
interfacefor props,typefor unions- Custom hooks for reusable logic (data fetching, auth, forms)
- React Hook Form + Zod for form validation
- TanStack Query for server state (not useState for API data)
- Context only for global state (auth, theme)
- React.memo for expensive renders only
- Error boundaries for crash recovery
- Suspense for loading states
- useCallback/useMemo only when needed (not by default)
- Clean up effects (return cleanup function)