# React Patterns and Best Practices ## Purpose Provides modern React patterns including: - Component organization - Hooks best practices - State management patterns - TypeScript integration - Performance optimization ## Component Structure ```typescript // src/components/UserProfile/UserProfile.tsx import { FC, memo } from 'react'; import { useUserProfile } from './useUserProfile'; import { UserProfileProps } from './types'; import styles from './UserProfile.module.css'; export const UserProfile: FC = memo(({ userId }) => { const { user, isLoading, error } = useUserProfile(userId); if (isLoading) return ; if (error) return ; if (!user) return null; return (

{user.name}

{user.email}

); }); UserProfile.displayName = 'UserProfile'; ``` ## Custom Hooks ```typescript // src/hooks/useAsync.ts import { useState, useCallback } from 'react'; interface AsyncState { data: T | null; isLoading: boolean; error: Error | null; } export function useAsync() { const [state, setState] = useState>({ data: null, isLoading: false, error: null, }); const execute = useCallback(async (promise: Promise) => { setState({ data: null, isLoading: true, error: null }); try { const data = await promise; setState({ data, isLoading: false, error: null }); return data; } catch (error) { setState({ data: null, isLoading: false, error: error as Error }); throw error; } }, []); return { ...state, execute }; } ``` ## API Hook Pattern ```typescript // src/hooks/useApi.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { api } from '../api'; export function useUsers() { return useQuery({ queryKey: ['users'], queryFn: api.getUsers, staleTime: 5 * 60 * 1000, // 5 minutes }); } export function useCreateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: api.createUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); } ``` ## Context Pattern ```typescript // src/context/AuthContext.tsx import { createContext, useContext, useState, ReactNode } from 'react'; interface AuthContextType { user: User | null; login: (credentials: Credentials) => Promise; logout: () => void; isAuthenticated: boolean; } const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const login = async (credentials: Credentials) => { const user = await authApi.login(credentials); setUser(user); }; const logout = () => { setUser(null); authApi.logout(); }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within AuthProvider'); } return context; } ``` ## Form Pattern (React Hook Form) ```typescript // src/components/UserForm/UserForm.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(2, 'Name must be at least 2 characters'), email: z.string().email('Invalid email address'), }); type FormData = z.infer; export function UserForm({ onSubmit }: { onSubmit: (data: FormData) => void }) { const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(schema), }); return (
{errors.name && {errors.name.message}} {errors.email && {errors.email.message}}
); } ``` ## Error Boundary ```typescript // src/components/ErrorBoundary.tsx import { Component, ErrorInfo, ReactNode } from 'react'; interface Props { children: ReactNode; fallback?: ReactNode; } interface State { hasError: boolean; error?: Error; } export class ErrorBoundary extends Component { state: State = { hasError: false }; static getDerivedStateFromError(error: Error): State { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('Error caught by boundary:', error, errorInfo); } render() { if (this.state.hasError) { return this.props.fallback ||
Something went wrong
; } return this.props.children; } } ``` ## Performance Patterns ```typescript // Memoization const ExpensiveComponent = memo(({ data }) => { // Only re-renders when data changes return
{/* ... */}
; }); // useMemo for expensive calculations const sortedItems = useMemo(() => { return items.sort((a, b) => a.name.localeCompare(b.name)); }, [items]); // useCallback for stable function references const handleClick = useCallback((id: string) => { setSelected(id); }, []); // Lazy loading const HeavyComponent = lazy(() => import('./HeavyComponent')); }> ``` ## Key Principles 1. **Composition over inheritance** - Compose components 2. **Single responsibility** - One component, one job 3. **Lift state up** - Share state at common ancestor 4. **Custom hooks** - Extract reusable logic 5. **TypeScript first** - Type everything 6. **Memoize wisely** - Don't over-optimize 7. **Error boundaries** - Graceful error handling