9.2 KiB
name, description, review, allowed-tools, version
| name | description | review | allowed-tools | version |
|---|---|---|---|---|
| reviewing-hook-patterns | Review React hook usage for React 19 compliance and best practices | true | Read, Grep, Glob | 1.0.0 |
Review: React Hook Patterns
Reviews React hook patterns for React 19 compliance, deprecated APIs, and best practices.
Activates: Code reviews, compliance validation, React 19 migration checks, hook-related PRs
Scope: New hooks (use(), useActionState, useOptimistic), deprecated patterns (forwardRef, propTypes, defaultProps), hook rules, best practices, TypeScript compliance
Focus: Correctness, security (Server Actions), performance (re-renders), React 19 compliance
Review Process
Phase 1: Find Deprecated APIs
Use Grep (output_mode: "content"):
forwardRef\.propTypes\s*=(removed in React 19)\.defaultProps\s*=(deprecated on function components)
Phase 2: Validate Hook Usage
use() API
- ✅ Promises or Context, Suspense-wrapped (Promises), Error Boundary (Promises)
- ❌ NOT in try-catch; Promises must be stable (outside component)
useActionState
- ✅ Server Action receives (previousState, formData), returns serializable values, has error handling, validates inputs server-side
- ❌ Missing authentication checks
useOptimistic
- ✅ Pure update function, paired with startTransition, visual pending indicator
- ❌ NOT for critical operations
Standard Hooks (useState, useEffect, etc.)
- ✅ Top-level calls, all dependencies included, cleanup functions for effects
- ❌ Missing dependencies, direct state mutation
Phase 3: TypeScript Validation
useRef requires initial value:
// ✅ Correct
const ref = useRef<HTMLDivElement>(null);
// ❌ Incorrect (React 19)
const ref = useRef<HTMLDivElement>();
Ref as prop typed correctly:
// ✅ Correct
interface Props {
ref?: Ref<HTMLButtonElement>;
}
// ❌ Incorrect (using forwardRef)
const Comp = forwardRef<HTMLButtonElement, Props>(...);
Phase 4: Identify Anti-Patterns
Array index as key:
// ❌ Bad
{
items.map((item, index) => <div key={index}>{item}</div>);
}
// ✅ Good
{
items.map((item) => <div key={item.id}>{item}</div>);
}
Direct state mutation:
// ❌ Bad
const [items, setItems] = useState([]);
items.push(newItem);
setItems(items);
// ✅ Good
setItems([...items, newItem]);
Missing dependencies:
// ❌ Bad
useEffect(() => {
fetchData(userId);
}, []);
// ✅ Good
useEffect(() => {
fetchData(userId);
}, [userId]);
Missing cleanup:
// ❌ Bad
useEffect(() => {
const timer = setInterval(() => {}, 1000);
}, []);
// ✅ Good
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
}, []);
Report Format
✅ Compliant Patterns
List correct React 19 usage, good practices, proper hooks.
⚠️ Warnings (Non-blocking)
Deprecated APIs still functional but require migration: forwardRef, manual memoization (when React Compiler available), patterns with better React 19 alternatives.
❌ Issues (Must Fix)
- Removed APIs: propTypes, defaultProps on function components, string refs
- Security: Unvalidated Server Actions, missing authentication, XSS vulnerabilities
- Hook Rules: Conditional calls, missing dependencies, hooks outside components
📝 Recommendations
Migration paths, performance improvements, best practices, relevant skill references.
Example Review: Form Component
Code:
import { useState } from 'react';
function ContactForm() {
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
async function handleSubmit(e) {
e.preventDefault();
await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify({ email, message }),
});
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<textarea value={message} onChange={(e) => setMessage(e.target.value)} />
<button type="submit">Send</button>
</form>
);
}
❌ Issues
- Missing Server Action Pattern — Use
useActionStatewith server validation instead of client-side fetch - No Validation — Add server-side validation (security risk)
- No Loading State — Use
isPendingfor UX feedback - No Error Handling — Return error state from Server Action
📝 Corrected Implementation
'use client';
import { useActionState } from 'react';
import { submitContact } from './actions';
function ContactForm() {
const [state, formAction, isPending] = useActionState(submitContact, null);
return (
<form action={formAction}>
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Sending...' : 'Send'}
</button>
{state?.error && <p className="error">{state.error}</p>}
{state?.success && <p>Message sent!</p>}
</form>
);
}
Server Action (actions.js):
'use server';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
message: z.string().min(10),
});
export async function submitContact(previousState, formData) {
const data = {
email: formData.get('email'),
message: formData.get('message'),
};
const result = schema.safeParse(data);
if (!result.success) return { error: 'Invalid input' };
try {
await db.contacts.create({ data: result.data });
return { success: true };
} catch (error) {
return { error: 'Failed to send message' };
}
}
Example Review: forwardRef Component
Code:
import { forwardRef } from 'react';
const Button = forwardRef((props, ref) => (
<button ref={ref} {...props}>
{props.children}
</button>
));
Button.displayName = 'Button';
⚠️ Warnings
Deprecated forwardRef Usage — Still functional in React 19 but deprecated. Migrate to ref-as-prop pattern.
📝 React 19 Migration
function Button({ children, ref, ...props }) {
return (
<button ref={ref} {...props}>
{children}
</button>
);
}
Benefits: Simpler API, better TypeScript inference, follows React 19 patterns, less boilerplate.
Review Standards
MUST Flag:
- Removed APIs (propTypes, defaultProps on functions, string refs)
- Hook rule violations (conditional calls, missing dependencies)
- Security issues (unvalidated Server Actions, missing auth)
- Missing Suspense/Error Boundaries for
use()with Promises
SHOULD Flag:
- Deprecated APIs (forwardRef)
- Performance issues (unnecessary re-renders, missing memoization when needed)
- Missing TypeScript types for React 19 patterns
- Anti-patterns (array index keys, direct state mutation)
MAY Suggest:
- Better React 19 patterns available
- Component architecture improvements
- Code organization enhancements
NEVER:
- Enforce personal style preferences
- Require changes that don't improve correctness/security/performance
- Flag patterns working correctly in React 19
- Demand premature optimization
Review Checklist
New React 19 Features
use()+ Promises: Suspense + Error Boundaryuse()+ Context: appropriate usageuseActionState: Server Actions validate inputsuseOptimistic: paired withstartTransitionuseFormStatus: inside form components
Deprecated Patterns
- No forwardRef or flagged for migration
- No propTypes on function components
- No defaultProps on function components
- No string refs
Hook Rules
- All hooks at top level
- No conditional hook calls
- All dependencies included
- Cleanup functions for subscriptions
TypeScript (if applicable)
useRefhas initial value- Ref props typed with
Ref<HTMLElement> - Server Actions properly typed
- No deprecated type patterns
Security
- Server Actions validate inputs
- Authentication checks present where needed
dangerouslySetInnerHTMLsanitized- No sensitive data exposed to client
Performance
- No array index as key
- No direct state mutation
- No missing dependencies (stale closures)
- Reasonable component structure
Common Issues Reference
| Issue | Search Pattern | Fix |
|---|---|---|
| forwardRef usage | forwardRef |
Convert to ref-as-prop |
| propTypes | \.propTypes\s*= |
Remove (use TypeScript) |
| defaultProps | \.defaultProps\s*= |
Use ES6 defaults |
| Missing dependencies | Manual review | Add to dependency array |
| Array index keys | key={.*index} |
Use stable ID |
| Direct mutation | Manual review | Use immutable updates |
| use() without Suspense | Manual review | Add Suspense boundary |
| Server Action no validation | Manual review | Add zod/yup validation |
For comprehensive React 19 patterns and migration guides, see: research/react-19-comprehensive.md