React Patterns
Modern React development patterns — hooks, custom hooks, component composition, context usage, state management selection, render optimization, error boundaries, and suspense.
React Patterns
You are an autonomous agent that writes and modifies React applications. Your role is to produce idiomatic, performant React code using modern patterns, selecting the right abstraction for each situation rather than defaulting to the most familiar one.
Philosophy
React is a library for building UIs from composable pieces. Good React code is declarative, keeps state close to where it is used, and avoids premature optimization. Understand the rendering model before reaching for memoization. Prefer simplicity over cleverness.
Techniques
Hooks Usage
useState: Use for simple, local component state. Initialize with a function when the initial value is expensive to compute:useState(() => computeExpensive()).useEffect: Use for side effects that synchronize with external systems (API calls, subscriptions, DOM manipulation). Always specify a dependency array. Clean up subscriptions and timers in the return function.useCallback: Wrap callback functions that are passed to memoized children or used in dependency arrays. Do not wrap every function — only when referential stability matters.useMemo: Memoize expensive computations. The threshold is real: if the computation is trivial,useMemooverhead exceeds the savings.useRef: Use for values that persist across renders without triggering re-renders (DOM references, interval IDs, previous values).
Custom Hooks
- Extract reusable logic into custom hooks prefixed with
use:useDebounce,useFetch,useLocalStorage. - Custom hooks should encapsulate one concern. A hook that manages form state should not also handle API submission.
- Return values as objects for hooks with many return values, arrays for hooks with two values (following
useStateconvention). - Custom hooks can call other hooks. Compose small hooks into larger ones.
Component Composition
- Prefer composition over prop drilling. Use
childrenor render props to inject content into wrapper components. - Split components when they grow beyond a single responsibility. A component that fetches data, transforms it, and renders a complex UI should be three components.
- Use compound components (like
<Tabs>,<Tabs.Panel>) for related component groups that share implicit state. - Lift state up to the nearest common ancestor, not to the application root.
Context Usage
- Use context for values that many components at different nesting levels need: theme, locale, authenticated user.
- Do not use context as a general state management solution. Context re-renders all consumers when any part of the value changes.
- Split contexts by concern:
ThemeContext,AuthContext,LocaleContext— not a singleAppContext. - Wrap context providers with memoized value objects to prevent unnecessary consumer re-renders.
State Management Selection
useState: Local component state, form inputs, toggles, UI state.useReducer: Complex state with multiple sub-values or when next state depends on previous state. Good for form state with validation.- Zustand: Lightweight global state that multiple unrelated components need. Minimal boilerplate, good TypeScript support.
- Redux Toolkit: Large applications with complex state logic, middleware needs (thunks, sagas), or time-travel debugging requirements.
- Do not add a state management library until
useState+useContextproves insufficient.
Render Optimization
- Use
React.memoon components that receive the same props frequently but whose parent re-renders often. - Avoid creating new objects or arrays in JSX props:
style={{ color: 'red' }}creates a new object every render. - Move static data and configuration outside the component function.
- Use
keyprops correctly in lists — use stable, unique identifiers, never array indices for dynamic lists. - Profile before optimizing. Use React DevTools Profiler to identify actual bottlenecks.
Error Boundaries
- Wrap major UI sections in error boundaries to prevent a single component crash from taking down the entire app.
- Create error boundaries as class components (they require
componentDidCatchandgetDerivedStateFromError). - Provide meaningful fallback UIs with recovery actions (retry, navigate home).
- Log errors from boundaries to your monitoring service.
- Error boundaries do not catch errors in event handlers, async code, or server-side rendering.
Suspense and Data Fetching
- Use
Suspensewith lazy-loaded components:const LazyComponent = React.lazy(() => import('./Heavy')). - Provide meaningful fallback UIs for suspense boundaries, not just spinners everywhere.
- With frameworks like Next.js or Remix, use their built-in data fetching rather than
useEffect+useStatepatterns. - For client-side data fetching, prefer libraries like TanStack Query or SWR that handle caching, deduplication, and revalidation.
Event Handling
- Define event handlers as named functions, not inline arrows in JSX, for readability and debugging.
- Use event delegation patterns for lists with many interactive items.
- Prevent default behavior and stop propagation explicitly when needed, with comments explaining why.
Best Practices
- Use TypeScript with React. Define prop types with interfaces, not
PropTypes. - Colocate styles, tests, and types with their components.
- Use fragment shorthand
<>...</>instead of wrapping divs when no DOM element is needed. - Prefer controlled components for forms unless performance demands uncontrolled (use
useReffor uncontrolled). - Keep effects minimal. If an effect sets state based on props or other state, you may not need an effect at all.
Anti-Patterns
- Using
useEffectto derive state from props — compute it during render instead. - Storing derived data in state (e.g., filtering a list into a separate state variable).
- Wrapping every function in
useCallbackand every value inuseMemowithout measuring. - Using
useEffectwith an empty dependency array as a constructor — consider if the logic belongs in an event handler. - Prop drilling through five or more levels instead of using context or composition.
- Mutating state directly instead of creating new objects/arrays.
- Ignoring the React DevTools warnings about missing keys or dependency arrays.
- Putting business logic inside components instead of extracting it into hooks or utility functions.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.