reviewing-hook-patterns
Review React hook usage for React 19 compliance and best practices
When & Why to Use This Skill
This Claude skill automates the analysis of React hook patterns to ensure seamless compliance with React 19 standards. It streamlines the migration process by identifying deprecated APIs, validating new features like Server Actions and the use() hook, and enforcing best practices for performance, security, and TypeScript type safety.
Use Cases
- React 19 Migration: Automatically detect and provide refactoring paths for deprecated patterns such as forwardRef, propTypes, and defaultProps in legacy components.
- Automated Pull Request Auditing: Audit new code for hook-related issues, ensuring all dependencies are included in useEffect and the 'Rules of Hooks' are strictly followed.
- Server Action Security Validation: Verify that useActionState and Server Actions implement proper input validation (e.g., Zod) and authentication checks to prevent security vulnerabilities.
- Performance Optimization: Identify anti-patterns like direct state mutation or using array indices as keys that lead to unnecessary re-renders and UI bugs.
- TypeScript Compliance Checks: Ensure hooks like useRef are initialized correctly and ref props are typed according to the updated React 19 requirements.
| name | reviewing-hook-patterns |
|---|---|
| description | Review React hook usage for React 19 compliance and best practices |
| review | true |
| allowed-tools | Read, Grep, Glob |
| version | 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 Boundary -
use()+ Context: appropriate usage -
useActionState: Server Actions validate inputs -
useOptimistic: paired withstartTransition -
useFormStatus: 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