typescript-react-reviewer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript + React 19 Code Review Expert
TypeScript + React 19 代码评审专家
Expert code reviewer with deep knowledge of React 19's new features, TypeScript best practices, state management patterns, and common anti-patterns.
拥有React 19新特性、TypeScript最佳实践、状态管理模式及常见反模式深度知识的专业代码评审专家。
Review Priority Levels
评审优先级等级
🚫 Critical (Block Merge)
🚫 严重(阻止合并)
These issues cause bugs, memory leaks, or architectural problems:
| Issue | Why It's Critical |
|---|---|
| Extra render cycle, sync bugs |
Missing cleanup in | Memory leaks |
Direct state mutation ( | Silent update failures |
| Conditional hook calls | Breaks Rules of Hooks |
| State corruption on reorder |
| Type safety bypass |
| Always returns false (React 19 bug) |
Promise created inside render with | Infinite loop |
这些问题会导致bug、内存泄漏或架构问题:
| 问题 | 严重原因 |
|---|---|
| 额外渲染周期、同步bug |
| 内存泄漏 |
直接修改状态( | 静默更新失败 |
| 条件式调用Hook | 违反Hook规则 |
动态列表中使用 | 重排序时状态损坏 |
无合理理由使用 | 绕过类型安全 |
在与 | 始终返回false(React 19 bug) |
在渲染中通过 | 无限循环 |
⚠️ High Priority
⚠️ 高优先级
| Issue | Impact |
|---|---|
| Incomplete dependency arrays | Stale closures, missing updates |
Props typed as | Runtime errors |
Unjustified | Unnecessary complexity |
| Missing Error Boundaries | Poor error UX |
Controlled input initialized with | React warning |
| 问题 | 影响 |
|---|---|
| 不完整的依赖数组 | 闭包过时、更新丢失 |
Props 类型设为 | 运行时错误 |
无合理理由使用 | 不必要的复杂度 |
| 缺少错误边界 | 糟糕的错误体验 |
受控输入初始化为 | React警告 |
📝 Architecture/Style
📝 架构/风格
| Issue | Recommendation |
|---|---|
| Component > 300 lines | Split into smaller components |
| Prop drilling > 2-3 levels | Use composition or context |
| State far from usage | Colocate state |
Custom hooks without | Follow naming convention |
| 问题 | 建议 |
|---|---|
| 组件代码超过300行 | 拆分为更小的组件 |
| 属性透传超过2-3层 | 使用组合或Context |
| 状态与使用位置相距过远 | 状态就近放置 |
自定义Hook未以 | 遵循命名规范 |
Quick Detection Patterns
快速检测模式
useEffect Abuse (Most Common Anti-Pattern)
useEffect 滥用(最常见反模式)
typescript
// ❌ WRONG: Derived state in useEffect
const [firstName, setFirstName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ✅ CORRECT: Compute during render
const fullName = firstName + ' ' + lastName;typescript
// ❌ WRONG: Event logic in useEffect
useEffect(() => {
if (product.isInCart) showNotification('Added!');
}, [product]);
// ✅ CORRECT: Logic in event handler
function handleAddToCart() {
addToCart(product);
showNotification('Added!');
}typescript
// ❌ 错误:在useEffect中处理派生状态
const [firstName, setFirstName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ✅ 正确:在渲染时计算
const fullName = firstName + ' ' + lastName;typescript
// ❌ 错误:在useEffect中处理事件逻辑
useEffect(() => {
if (product.isInCart) showNotification('Added!');
}, [product]);
// ✅ 正确:在事件处理函数中处理逻辑
function handleAddToCart() {
addToCart(product);
showNotification('Added!');
}React 19 Hook Mistakes
React 19 Hook 错误示例
typescript
// ❌ WRONG: useFormStatus in form component (always returns false)
function Form() {
const { pending } = useFormStatus();
return <form action={submit}><button disabled={pending}>Send</button></form>;
}
// ✅ CORRECT: useFormStatus in child component
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>Send</button>;
}
function Form() {
return <form action={submit}><SubmitButton /></form>;
}typescript
// ❌ WRONG: Promise created in render (infinite loop)
function Component() {
const data = use(fetch('/api/data')); // New promise every render!
}
// ✅ CORRECT: Promise from props or state
function Component({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
}typescript
// ❌ 错误:在表单组件中使用useFormStatus(始终返回false)
function Form() {
const { pending } = useFormStatus();
return <form action={submit}><button disabled={pending}>Send</button></form>;
}
// ✅ 正确:在子组件中使用useFormStatus
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>Send</button>;
}
function Form() {
return <form action={submit}><SubmitButton /></form>;
}typescript
// ❌ 错误:在渲染中创建Promise(无限循环)
function Component() {
const data = use(fetch('/api/data')); // 每次渲染都会创建新Promise!
}
// ✅ 正确:从props或state获取Promise
function Component({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
}State Mutation Detection
状态修改检测
typescript
// ❌ WRONG: Mutations (no re-render)
items.push(newItem);
setItems(items);
arr[i] = newValue;
setArr(arr);
// ✅ CORRECT: Immutable updates
setItems([...items, newItem]);
setArr(arr.map((x, idx) => idx === i ? newValue : x));typescript
// ❌ 错误:直接修改状态(不会触发重渲染)
items.push(newItem);
setItems(items);
arr[i] = newValue;
setArr(arr);
// ✅ 正确:不可变更新
setItems([...items, newItem]);
setArr(arr.map((x, idx) => idx === i ? newValue : x));TypeScript Red Flags
TypeScript 危险信号
typescript
// ❌ Red flags to catch
const data: any = response; // Unsafe any
const items = arr[10]; // Missing undefined check
const App: React.FC<Props> = () => {}; // Discouraged pattern
// ✅ Preferred patterns
const data: ResponseType = response;
const items = arr[10]; // with noUncheckedIndexedAccess
const App = ({ prop }: Props) => {}; // Explicit propstypescript
// ❌ 需要注意的危险信号
const data: any = response; // 不安全的any类型
const items = arr[10]; // 缺少undefined检查
const App: React.FC<Props> = () => {}; // 不推荐的模式
// ✅ 推荐模式
const data: ResponseType = response;
const items = arr[10]; // 配合noUncheckedIndexedAccess使用
const App = ({ prop }: Props) => {}; // 显式定义propsReview Workflow
评审工作流
- Scan for critical issues first - Check for the patterns in "Critical (Block Merge)" section
- Check React 19 usage - See react19-patterns.md for new API patterns
- Evaluate state management - Is state colocated? Server state vs client state separation?
- Assess TypeScript safety - Generic components, discriminated unions, strict config
- Review for maintainability - Component size, hook design, folder structure
- 首先扫描严重问题 - 检查「严重(阻止合并)」部分中的模式
- 检查React 19使用情况 - 参考react19-patterns.md了解新API模式
- 评估状态管理 - 状态是否就近放置?服务端状态与客户端状态是否分离?
- 评估TypeScript安全性 - 泛型组件、区分联合类型、严格配置
- 评审可维护性 - 组件大小、Hook设计、文件夹结构
Reference Documents
参考文档
For detailed patterns and examples:
- react19-patterns.md - React 19 new hooks (useActionState, useOptimistic, use), Server/Client Component boundaries
- antipatterns.md - Comprehensive anti-pattern catalog with fixes
- checklist.md - Full code review checklist for thorough reviews
如需详细模式和示例:
- react19-patterns.md - React 19新Hook(useActionState、useOptimistic、use)、服务端/客户端组件边界
- antipatterns.md - 包含修复方案的全面反模式目录
- checklist.md - 用于全面评审的完整代码评审检查清单
State Management Quick Guide
状态管理快速指南
| Data Type | Solution |
|---|---|
| Server/async data | TanStack Query (never copy to local state) |
| Simple global UI state | Zustand (~1KB, no Provider) |
| Fine-grained derived state | Jotai (~2.4KB) |
| Component-local state | useState/useReducer |
| Form state | React 19 useActionState |
| 数据类型 | 解决方案 |
|---|---|
| 服务端/异步数据 | TanStack Query(绝不要复制到本地状态) |
| 简单全局UI状态 | Zustand(约1KB,无需Provider) |
| 细粒度派生状态 | Jotai(约2.4KB) |
| 组件本地状态 | useState/useReducer |
| 表单状态 | React 19 useActionState |
TanStack Query Anti-Pattern
TanStack Query 反模式
typescript
// ❌ NEVER copy server data to local state
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
const [todos, setTodos] = useState([]);
useEffect(() => setTodos(data), [data]);
// ✅ Query IS the source of truth
const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });typescript
// ❌ 绝不要将服务端数据复制到本地状态
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
const [todos, setTodos] = useState([]);
useEffect(() => setTodos(data), [data]);
// ✅ Query就是唯一数据源
const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });TypeScript Config Recommendations
TypeScript 配置建议
json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"exactOptionalPropertyTypes": true
}
}noUncheckedIndexedAccessarr[i]json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"exactOptionalPropertyTypes": true
}
}noUncheckedIndexedAccessarr[i]Immediate Red Flags
立即需要标记的危险信号
When reviewing, flag these immediately:
| Pattern | Problem | Fix |
|---|---|---|
| Hides stale closure bugs | Refactor logic |
| Component defined inside component | Remounts every render | Move outside |
| Uncontrolled warning | Use empty string |
| Generic inference breaks | Use explicit props |
Barrel files ( | Bundle bloat, circular deps | Direct imports |
评审时,需立即标记以下模式:
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 隐藏闭包过时bug | 重构逻辑 |
| 在组件内部定义组件 | 每次渲染都会重新挂载 | 移到外部 |
输入框使用 | 非受控组件警告 | 使用空字符串 |
泛型组件使用 | 泛型推断失效 | 使用显式props定义 |
应用代码中使用桶文件( | 包体积膨胀、循环依赖 | 使用直接导入 |