(
+ Mouse position: {x}, {y}
+ )}
+/>
+```
+
+### Higher-Order Components (HOC)
+
+Use HOCs for cross-cutting concerns like authentication or logging.
+
+```tsx
+function withAuth(WrappedComponent: React.ComponentType
) {
+ return function AuthenticatedComponent(props: P) {
+ const { user, isLoading } = useAuth();
+
+ if (isLoading) return ;
+ if (!user) return ;
+
+ return ;
+ };
+}
+
+// Usage
+const ProtectedDashboard = withAuth(Dashboard);
+```
+
+---
+
+## Custom Hooks
+
+### useAsync - Handle async operations
+
+```tsx
+interface AsyncState {
+ data: T | null;
+ error: Error | null;
+ status: 'idle' | 'loading' | 'success' | 'error';
+}
+
+function useAsync(asyncFn: () => Promise, deps: any[] = []) {
+ const [state, setState] = useState>({
+ data: null,
+ error: null,
+ status: 'idle',
+ });
+
+ const execute = useCallback(async () => {
+ setState({ data: null, error: null, status: 'loading' });
+ try {
+ const data = await asyncFn();
+ setState({ data, error: null, status: 'success' });
+ } catch (error) {
+ setState({ data: null, error: error as Error, status: 'error' });
+ }
+ }, deps);
+
+ useEffect(() => {
+ execute();
+ }, [execute]);
+
+ return { ...state, refetch: execute };
+}
+
+// Usage
+function UserProfile({ userId }: { userId: string }) {
+ const { data: user, status, error, refetch } = useAsync(
+ () => fetchUser(userId),
+ [userId]
+ );
+
+ if (status === 'loading') return ;
+ if (status === 'error') return ;
+ if (!user) return null;
+
+ return ;
+}
+```
+
+### useDebounce - Debounce values
+
+```tsx
+function useDebounce(value: T, delay: number): T {
+ const [debouncedValue, setDebouncedValue] = useState(value);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
+ return () => clearTimeout(timer);
+ }, [value, delay]);
+
+ return debouncedValue;
+}
+
+// Usage
+function SearchInput() {
+ const [query, setQuery] = useState('');
+ const debouncedQuery = useDebounce(query, 300);
+
+ useEffect(() => {
+ if (debouncedQuery) {
+ searchAPI(debouncedQuery);
+ }
+ }, [debouncedQuery]);
+
+ return setQuery(e.target.value)} />;
+}
+```
+
+### useLocalStorage - Persist state
+
+```tsx
+function useLocalStorage(key: string, initialValue: T) {
+ const [storedValue, setStoredValue] = useState(() => {
+ if (typeof window === 'undefined') return initialValue;
+ try {
+ const item = window.localStorage.getItem(key);
+ return item ? JSON.parse(item) : initialValue;
+ } catch {
+ return initialValue;
+ }
+ });
+
+ const setValue = useCallback((value: T | ((val: T) => T)) => {
+ try {
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
+ setStoredValue(valueToStore);
+ if (typeof window !== 'undefined') {
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
+ }
+ } catch (error) {
+ console.error('Error saving to localStorage:', error);
+ }
+ }, [key, storedValue]);
+
+ return [storedValue, setValue] as const;
+}
+
+// Usage
+const [theme, setTheme] = useLocalStorage('theme', 'light');
+```
+
+### useMediaQuery - Responsive design
+
+```tsx
+function useMediaQuery(query: string): boolean {
+ const [matches, setMatches] = useState(false);
+
+ useEffect(() => {
+ const media = window.matchMedia(query);
+ setMatches(media.matches);
+
+ const listener = (e: MediaQueryListEvent) => setMatches(e.matches);
+ media.addEventListener('change', listener);
+ return () => media.removeEventListener('change', listener);
+ }, [query]);
+
+ return matches;
+}
+
+// Usage
+function ResponsiveNav() {
+ const isMobile = useMediaQuery('(max-width: 768px)');
+ return isMobile ? : ;
+}
+```
+
+### usePrevious - Track previous values
+
+```tsx
+function usePrevious(value: T): T | undefined {
+ const ref = useRef();
+
+ useEffect(() => {
+ ref.current = value;
+ }, [value]);
+
+ return ref.current;
+}
+
+// Usage
+function Counter() {
+ const [count, setCount] = useState(0);
+ const prevCount = usePrevious(count);
+
+ return (
+
+ Current: {count}, Previous: {prevCount}
+
+ );
+}
+```
+
+---
+
+## State Management
+
+### Context with Reducer
+
+For complex state that multiple components need to access.
+
+```tsx
+// types.ts
+interface CartItem {
+ id: string;
+ name: string;
+ price: number;
+ quantity: number;
+}
+
+interface CartState {
+ items: CartItem[];
+ total: number;
+}
+
+type CartAction =
+ | { type: 'ADD_ITEM'; payload: CartItem }
+ | { type: 'REMOVE_ITEM'; payload: string }
+ | { type: 'UPDATE_QUANTITY'; payload: { id: string; quantity: number } }
+ | { type: 'CLEAR_CART' };
+
+// reducer.ts
+function cartReducer(state: CartState, action: CartAction): CartState {
+ switch (action.type) {
+ case 'ADD_ITEM': {
+ const existingItem = state.items.find(i => i.id === action.payload.id);
+ if (existingItem) {
+ return {
+ ...state,
+ items: state.items.map(item =>
+ item.id === action.payload.id
+ ? { ...item, quantity: item.quantity + 1 }
+ : item
+ ),
+ };
+ }
+ return {
+ ...state,
+ items: [...state.items, { ...action.payload, quantity: 1 }],
+ };
+ }
+ case 'REMOVE_ITEM':
+ return {
+ ...state,
+ items: state.items.filter(i => i.id !== action.payload),
+ };
+ case 'UPDATE_QUANTITY':
+ return {
+ ...state,
+ items: state.items.map(item =>
+ item.id === action.payload.id
+ ? { ...item, quantity: action.payload.quantity }
+ : item
+ ),
+ };
+ case 'CLEAR_CART':
+ return { items: [], total: 0 };
+ default:
+ return state;
+ }
+}
+
+// context.tsx
+const CartContext = createContext<{
+ state: CartState;
+ dispatch: React.Dispatch;
+} | null>(null);
+
+function CartProvider({ children }: { children: React.ReactNode }) {
+ const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
+
+ // Compute total whenever items change
+ const stateWithTotal = useMemo(() => ({
+ ...state,
+ total: state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
+ }), [state.items]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function useCart() {
+ const context = useContext(CartContext);
+ if (!context) throw new Error('useCart must be used within CartProvider');
+ return context;
+}
+```
+
+### Zustand (Lightweight Alternative)
+
+```tsx
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+interface AuthStore {
+ user: User | null;
+ token: string | null;
+ login: (email: string, password: string) => Promise;
+ logout: () => void;
+}
+
+const useAuthStore = create()(
+ persist(
+ (set) => ({
+ user: null,
+ token: null,
+ login: async (email, password) => {
+ const { user, token } = await authAPI.login(email, password);
+ set({ user, token });
+ },
+ logout: () => set({ user: null, token: null }),
+ }),
+ { name: 'auth-storage' }
+ )
+);
+
+// Usage
+function Profile() {
+ const { user, logout } = useAuthStore();
+ return user ? {user.name}
: null;
+}
+```
+
+---
+
+## Performance Patterns
+
+### React.memo with Custom Comparison
+
+```tsx
+interface ListItemProps {
+ item: { id: string; name: string; count: number };
+ onSelect: (id: string) => void;
+}
+
+const ListItem = React.memo(
+ function ListItem({ item, onSelect }: ListItemProps) {
+ return (
+ onSelect(item.id)}>
+ {item.name} ({item.count})
+
+ );
+ },
+ (prevProps, nextProps) => {
+ // Only re-render if item data changed
+ return (
+ prevProps.item.id === nextProps.item.id &&
+ prevProps.item.name === nextProps.item.name &&
+ prevProps.item.count === nextProps.item.count
+ );
+ }
+);
+```
+
+### useMemo for Expensive Calculations
+
+```tsx
+function DataTable({ data, sortColumn, filterText }: {
+ data: Item[];
+ sortColumn: string;
+ filterText: string;
+}) {
+ const processedData = useMemo(() => {
+ // Filter
+ let result = data.filter(item =>
+ item.name.toLowerCase().includes(filterText.toLowerCase())
+ );
+
+ // Sort
+ result = [...result].sort((a, b) => {
+ const aVal = a[sortColumn as keyof Item];
+ const bVal = b[sortColumn as keyof Item];
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
+ });
+
+ return result;
+ }, [data, sortColumn, filterText]);
+
+ return (
+
+ {processedData.map(item => (
+ {/* ... */}
+ ))}
+
+ );
+}
+```
+
+### useCallback for Stable References
+
+```tsx
+function ParentComponent() {
+ const [items, setItems] = useState- ([]);
+
+ // Stable reference - won't cause child re-renders
+ const handleItemClick = useCallback((id: string) => {
+ setItems(prev => prev.map(item =>
+ item.id === id ? { ...item, selected: !item.selected } : item
+ ));
+ }, []);
+
+ const handleAddItem = useCallback((newItem: Item) => {
+ setItems(prev => [...prev, newItem]);
+ }, []);
+
+ return (
+ <>
+
+
+ >
+ );
+}
+```
+
+### Virtualization for Long Lists
+
+```tsx
+import { useVirtualizer } from '@tanstack/react-virtual';
+
+function VirtualList({ items }: { items: Item[] }) {
+ const parentRef = useRef(null);
+
+ const virtualizer = useVirtualizer({
+ count: items.length,
+ getScrollElement: () => parentRef.current,
+ estimateSize: () => 50, // estimated row height
+ overscan: 5,
+ });
+
+ return (
+
+
+ {virtualizer.getVirtualItems().map(virtualRow => (
+
+ {items[virtualRow.index].name}
+
+ ))}
+
+
+ );
+}
+```
+
+---
+
+## Error Boundaries
+
+### Class-Based Error Boundary
+
+```tsx
+interface ErrorBoundaryProps {
+ children: React.ReactNode;
+ fallback?: React.ReactNode;
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
+}
+
+interface ErrorBoundaryState {
+ hasError: boolean;
+ error: Error | null;
+}
+
+class ErrorBoundary extends React.Component {
+ state: ErrorBoundaryState = { hasError: false, error: null };
+
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
+ this.props.onError?.(error, errorInfo);
+ // Log to error reporting service
+ console.error('Error caught:', error, errorInfo);
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return this.props.fallback || (
+
+
Something went wrong
+
{this.state.error?.message}
+
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
+
+// Usage
+}
+ onError={(error) => trackError(error)}
+>
+
+
+```
+
+### Suspense with Error Boundary
+
+```tsx
+function DataComponent() {
+ return (
+ }>
+ }>
+
+