--- name: review-react description: Reviews React and TypeScript code for correctness, security, hook usage, and multi-tenancy safety. Use when reviewing generated React components, hooks, or TypeScript files, or when asked to do a code review on frontend code. --- # React Code Review Targets **React 19** with the React Compiler. Work through the five areas in order. For the full pass/fail checklist, see [checklist.md](checklist.md). ## 1. Component patterns - Functional components only — no class components - One clear responsibility per component; extract if JSX exceeds ~100 lines - No business logic or data-fetching directly inside JSX - Props typed with explicit interfaces — no `any`, no implicit `{}` - No component defined inside another component body - List keys must be stable and unique (never array index for dynamic lists) ## 2. Hooks **Core rules (non-negotiable)** - Never call hooks conditionally or inside loops — except `use()`, which is designed for conditional calls - Enable `react-hooks/rules-of-hooks` and `react-hooks/exhaustive-deps` ESLint rules; treat all warnings as errors **React 19 hook additions to know** | Hook | Use for | | ----------------------- | ------------------------------------------------------------------------------------ | | `use(promise\|context)` | Reading context or async values; can be called conditionally | | `useActionState` | Form submission state + pending flag — replaces manual `useState` + loading patterns | | `useFormStatus` | Child components reading parent form submission state | | `useOptimistic` | Optimistic UI updates before server confirmation | **Effects** - Effects are for syncing with external systems only — not for pure computations - `useEffect` deps array must be exhaustive; missing deps hide stale closures - Fetch cleanup must use `AbortController`, not just a `mounted` flag: ```tsx useEffect(() => { const ac = new AbortController(); fetch(`/api/notes/${id}`, { signal: ac.signal }) .then((r) => r.json()) .then(setNote) .catch((e) => { if (e.name !== "AbortError") setError(e); }); return () => ac.abort(); }, [id]); ``` - `useLayoutEffect` only for DOM measurements or synchronizing layout — never as a first choice - Stale values that must not re-trigger effects → store in a `useRef`, read `ref.current` inside the effect ## 3. Performance (React 19 compiler-aware) The React Compiler automatically memoizes at build time. **Do not add `useMemo` / `useCallback` / `React.memo` by default.** Add manual memoization only in these three cases: 1. Third-party libraries that require stable references 2. Effects with function dependencies that would cause infinite loops without stabilization 3. Genuinely expensive computations with external / non-React data Use `useTransition` to keep urgent interactions (typing, gestures) responsive while deferring heavy renders. Use `useDeferredValue` to defer a derived value to a slower render. ## 4. Security - `dangerouslySetInnerHTML` with any user-supplied string → **critical bug**; must sanitize with DOMPurify first - `href={userInput}` → **critical bug**; validate URL starts with `https://` or use an allowlist - Auth tokens stored in `localStorage` → **critical bug**; must use `httpOnly` cookies - No secrets, API keys, or internal IDs hardcoded in client source - No sensitive data logged to `console` in production paths - Content Security Policy headers enforced at the infrastructure/server level ## 5. Multi-tenancy safety - `orgId` / `tenantId` used in API calls must come from the **server-side session** (token/cookie), never from URL params or component state alone - Switching orgs must **destroy and re-initialize the entire tenant context**, not just invalidate queries - Access checks happen on the API — UI hiding (conditional rendering, CSS) is UX only, never security - File/note access must not rely on the client passing its own permission level ## Additional resources - Full pass/fail line-item checklist → [checklist.md](checklist.md)