关于 typescript-react-reviewer
typescript-react-reviewer 是面向 TypeScript 与 React 19 项目的代码审查技能,适合开发者和审查者。它按严重级别检查 Hook、状态管理、类型安全与架构问题,并提供参考清单。
主要功能
- 01按优先级识别问题
- 02检查 React 19 Hook 用法
- 03发现状态变更反模式
- 04评估 TypeScript 类型安全
- 05提供审查工作流与清单
适用场景
- 01合并前排查关键缺陷
- 02检查 Hook 依赖与清理逻辑
- 03评估组件结构和状态管理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dotneet/ claude-code-marketplace --skill typescript-react-reviewer安装后在 Claude Code 中输入/typescript-react-reviewer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/antipatterns.mdchecklist.mdreact19-patterns.md详细介绍
TypeScript + React 19 代码审查专家
精通 React 19 新特性、TypeScript 最佳实践、状态管理模式及常见反模式的代码审查专家。
审查优先级
🚫 严重(阻止合并)
这些问题会导致错误、内存泄漏或架构问题:
| 问题 | 严重原因 |
|---|---|
使用 useEffect 处理派生状态 | 产生额外的渲染周期、引发同步错误 |
useEffect 缺少清理函数 | 内存泄漏 |
直接修改状态(.push()、.splice()) | 更新静默失败 |
| 有条件地调用 Hook | 违反 Hook 规则 |
动态列表中使用 key={index} | 重排时状态错乱 |
使用 any 类型且没有说明理由 | 绕过类型安全检查 |
在与 <form> 相同的组件中使用 useFormStatus | 始终返回 false(React 19 的 Bug) |
在渲染期间创建供 use() 使用的 Promise | 无限循环 |
⚠️ 高优先级
| 问题 | 影响 |
|---|---|
| 依赖数组不完整 | 闭包过时、缺少更新 |
将 Props 类型标注为 any | 运行时错误 |
使用 useMemo/useCallback 没有充分理由 | 增加不必要的复杂性 |
| 缺少错误边界 | 错误体验不佳 |
受控输入初始化为 undefined | React 警告 |
📝 架构/风格
| 问题 | 建议 |
|---|---|
| 组件超过 300 行 | 拆分为更小的组件 |
| Props 逐层传递超过 2-3 层 | 使用组合或上下文 |
| 状态离使用位置太远 | 将状态放到使用位置附近 |
自定义 Hook 名称没有 use 前缀 | 遵循命名约定 |
快速检测模式
滥用 useEffect(最常见的反模式)
// ❌ 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;
// ❌ WRONG: Event logic in useEffect
useEffect(() => {
if (product.isInCart) showNotification('Added!');
}, [product]);
// ✅ CORRECT: Logic in event handler
function handleAddToCart() {
addToCart(product);
showNotification('Added!');
}
React 19 Hook 常见错误
// ❌ 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>;
}
// ❌ 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);
}
状态修改检测
// ❌ 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 危险信号
// ❌ 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
审查工作流
- 首先扫描严重问题 - 检查“严重(阻止合并)”部分中的模式
- 检查 React 19 的用法 - 查看 react19-patterns.md,了解新的 API 模式
- 评估状态管理 - 状态是否放在合适的位置?服务端状态与客户端状态是否分离?
- 评估 TypeScript 安全性 - 泛型组件、可辨识联合类型、严格配置
- 检查可维护性 - 组件大小、Hook 设计、文件夹结构
参考文档
详细模式和示例请参阅:
- react19-patterns.md - React 19 新 Hook(useActionState、useOptimistic、use)、服务端/客户端组件边界
- antipatterns.md - 全面的反模式目录及修复方法
- checklist.md - 用于彻底审查的完整代码审查清单
状态管理快速指南
| 数据类型 | 方案 |
|---|---|
| 服务端/异步数据 | TanStack Query(绝不复制到本地状态) |
| 简单的全局 UI 状态 | Zustand(约 1KB,无需 Provider) |
| 细粒度派生状态 | Jotai(约 2.4KB) |
| 组件局部状态 | useState/useReducer |
| 表单状态 | React 19 useActionState |
TanStack Query 反模式
// ❌ 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 配置建议
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"exactOptionalPropertyTypes": true
}
}
noUncheckedIndexedAccess 非常关键——它能捕获 arr[i] 返回 undefined 的情况。
需要立即关注的危险信号
审查时,遇到以下情况请立即标记:
| 模式 | 问题 | 修复方法 |
|---|---|---|
eslint-disable react-hooks/exhaustive-deps | 掩盖闭包过时问题 | 重构逻辑 |
| 在组件内部定义组件 | 每次渲染都会重新挂载 | 移到外部 |
对输入使用 useState(undefined) | 未受控组件警告 | 使用空字符串 |
泛型与 React.FC 一起使用 | 泛型推断失效 | 使用显式 Props |
在应用代码中使用聚合文件(index.ts) | 增加打包体积、产生循环依赖 | 直接导入 |
评论
支持 Markdown