typescript-react-reviewer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript + 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:
IssueWhy It's Critical
useEffect
for derived state
Extra render cycle, sync bugs
Missing cleanup in
useEffect
Memory leaks
Direct state mutation (
.push()
,
.splice()
)
Silent update failures
Conditional hook callsBreaks Rules of Hooks
key={index}
in dynamic lists
State corruption on reorder
any
type without justification
Type safety bypass
useFormStatus
in same component as
<form>
Always returns false (React 19 bug)
Promise created inside render with
use()
Infinite loop
这些问题会导致bug、内存泄漏或架构问题:
问题严重原因
useEffect
用于派生状态
额外渲染周期、同步bug
useEffect
中缺少清理逻辑
内存泄漏
直接修改状态(
.push()
,
.splice()
静默更新失败
条件式调用Hook违反Hook规则
动态列表中使用
key={index}
重排序时状态损坏
无合理理由使用
any
类型
绕过类型安全
在与
<form>
同一组件中使用
useFormStatus
始终返回false(React 19 bug)
在渲染中通过
use()
创建Promise
无限循环

⚠️ High Priority

⚠️ 高优先级

IssueImpact
Incomplete dependency arraysStale closures, missing updates
Props typed as
any
Runtime errors
Unjustified
useMemo
/
useCallback
Unnecessary complexity
Missing Error BoundariesPoor error UX
Controlled input initialized with
undefined
React warning
问题影响
不完整的依赖数组闭包过时、更新丢失
Props 类型设为
any
运行时错误
无合理理由使用
useMemo
/
useCallback
不必要的复杂度
缺少错误边界糟糕的错误体验
受控输入初始化为
undefined
React警告

📝 Architecture/Style

📝 架构/风格

IssueRecommendation
Component > 300 linesSplit into smaller components
Prop drilling > 2-3 levelsUse composition or context
State far from usageColocate state
Custom hooks without
use
prefix
Follow naming convention
问题建议
组件代码超过300行拆分为更小的组件
属性透传超过2-3层使用组合或Context
状态与使用位置相距过远状态就近放置
自定义Hook未以
use
开头命名
遵循命名规范

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 props
typescript
// ❌ 需要注意的危险信号
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) => {};  // 显式定义props

Review Workflow

评审工作流

  1. Scan for critical issues first - Check for the patterns in "Critical (Block Merge)" section
  2. Check React 19 usage - See react19-patterns.md for new API patterns
  3. Evaluate state management - Is state colocated? Server state vs client state separation?
  4. Assess TypeScript safety - Generic components, discriminated unions, strict config
  5. Review for maintainability - Component size, hook design, folder structure
  1. 首先扫描严重问题 - 检查「严重(阻止合并)」部分中的模式
  2. 检查React 19使用情况 - 参考react19-patterns.md了解新API模式
  3. 评估状态管理 - 状态是否就近放置?服务端状态与客户端状态是否分离?
  4. 评估TypeScript安全性 - 泛型组件、区分联合类型、严格配置
  5. 评审可维护性 - 组件大小、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 TypeSolution
Server/async dataTanStack Query (never copy to local state)
Simple global UI stateZustand (~1KB, no Provider)
Fine-grained derived stateJotai (~2.4KB)
Component-local stateuseState/useReducer
Form stateReact 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
  }
}
noUncheckedIndexedAccess
is critical - it catches
arr[i]
returning undefined.
json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "exactOptionalPropertyTypes": true
  }
}
noUncheckedIndexedAccess
至关重要 - 它会捕获
arr[i]
返回undefined的情况。

Immediate Red Flags

立即需要标记的危险信号

When reviewing, flag these immediately:
PatternProblemFix
eslint-disable react-hooks/exhaustive-deps
Hides stale closure bugsRefactor logic
Component defined inside componentRemounts every renderMove outside
useState(undefined)
for inputs
Uncontrolled warningUse empty string
React.FC
with generics
Generic inference breaksUse explicit props
Barrel files (
index.ts
) in app code
Bundle bloat, circular depsDirect imports
评审时,需立即标记以下模式:
模式问题修复方案
eslint-disable react-hooks/exhaustive-deps
隐藏闭包过时bug重构逻辑
在组件内部定义组件每次渲染都会重新挂载移到外部
输入框使用
useState(undefined)
初始化
非受控组件警告使用空字符串
泛型组件使用
React.FC
泛型推断失效使用显式props定义
应用代码中使用桶文件(
index.ts
包体积膨胀、循环依赖使用直接导入