Files
2025-11-29 18:22:28 +08:00

365 lines
9.2 KiB
Markdown

---
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:
```typescript
// ✅ Correct
const ref = useRef<HTMLDivElement>(null);
// ❌ Incorrect (React 19)
const ref = useRef<HTMLDivElement>();
```
**Ref as prop** typed correctly:
```typescript
// ✅ Correct
interface Props {
ref?: Ref<HTMLButtonElement>;
}
// ❌ Incorrect (using forwardRef)
const Comp = forwardRef<HTMLButtonElement, Props>(...);
```
### Phase 4: Identify Anti-Patterns
**Array index as key:**
```javascript
// ❌ Bad
{
items.map((item, index) => <div key={index}>{item}</div>);
}
// ✅ Good
{
items.map((item) => <div key={item.id}>{item}</div>);
}
```
**Direct state mutation:**
```javascript
// ❌ Bad
const [items, setItems] = useState([]);
items.push(newItem);
setItems(items);
// ✅ Good
setItems([...items, newItem]);
```
**Missing dependencies:**
```javascript
// ❌ Bad
useEffect(() => {
fetchData(userId);
}, []);
// ✅ Good
useEffect(() => {
fetchData(userId);
}, [userId]);
```
**Missing cleanup:**
```javascript
// ❌ 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:**
```javascript
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
1. **Missing Server Action Pattern** — Use `useActionState` with server validation instead of client-side fetch
2. **No Validation** — Add server-side validation (security risk)
3. **No Loading State** — Use `isPending` for UX feedback
4. **No Error Handling** — Return error state from Server Action
### 📝 Corrected Implementation
```javascript
'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):**
```javascript
'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:**
```javascript
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
```javascript
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 with `startTransition`
- [ ] `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)
- [ ] `useRef` has 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
- [ ] `dangerouslySetInnerHTML` sanitized
- [ ] 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`