---
name: react-best-practices
description: Use when reading or writing React components (.tsx, .jsx files with React imports).
---
# React Best Practices
## Pair with TypeScript
When working with React, always load both this skill and `typescript-best-practices` together. TypeScript patterns (type-first development, discriminated unions, Zod validation) apply to React code.
## Core Principle: Effects Are Escape Hatches
Effects let you "step outside" React to synchronize with external systems. **Most component logic should NOT use Effects.** Before writing an Effect, ask: "Is there a way to do this without an Effect?"
## Decision Tree
1. **Need to respond to user interaction?** Use event handler
2. **Need computed value from props/state?** Calculate during render
3. **Need cached expensive calculation?** Use `useMemo`
4. **Need to reset state on prop change?** Use `key` prop
5. **Need to synchronize with external system?** Use Effect with cleanup
6. **Need non-reactive code in Effect?** Use `useEffectEvent`
7. **Need mutable value that doesn't trigger render?** Use ref
## When to Use Effects
Synchronizing with **external systems**: browser APIs (WebSocket, IntersectionObserver), third-party non-React libraries, window/document event listeners, non-React DOM elements (video, maps).
## When NOT to Use Effects
- Derived state — calculate during render
- Expensive calculations — use `useMemo`
- Resetting state on prop change — use `key` prop
- Responding to user events — use event handlers
- Notifying parent of state changes — update both in the same event handler
- Chains of effects — calculate derived state and update in one event handler
## Refs
- Use for values that don't affect rendering (timer IDs, DOM node references)
- Never read or write `ref.current` during render; only in event handlers and effects
- Use ref callbacks (not `useRef` in loops) for dynamic lists
- Use `useImperativeHandle` to limit what parent can access
## Custom Hooks
- Share logic, not state — each call gets an independent state instance
- Name `useXxx` only if it actually calls other hooks; otherwise use a regular function
- Avoid lifecycle hooks (`useMount`, `useEffectOnce`) — use `useEffect` directly so the linter catches missing deps
- Keep focused on a single concrete use case
## Component Patterns
- Controlled: parent owns state; uncontrolled: component owns state
- Prefer composition with `children` over prop drilling
- Treat boolean props that switch large component trees (`isEditing`, `isThread`, `hideAttachments`) as a composition smell; prefer separate composed components for distinct use cases
- For complex reusable UI, prefer compound components with provider-scoped state/actions over monolithic components with many optional props
- Use Context for scoped component families as well as truly global state, when it defines a local interface consumed by descendants
- Render JSX directly for UI variation; avoid config-array mini-frameworks unless the config is real domain data
- Lift the provider boundary when sibling or external controls need access to the same state/actions
- Use `flushSync` when you need to read the DOM synchronously after a state update
See `react-patterns.md` for code examples and detailed patterns.