mcpField
R
react-code-review

react-code-review

github.com/giuseppe-trisciuoglio测试与质量3654.4k 次安装7 周前更新
代码仓库

关于 react-code-review

面向 React 开发者的结构化代码审查技能,适用于合并前检查或重构后评估。报告按严重程度分类的问题、正向发现及附带示例的优先建议。

主要功能

  1. 01检查 React 组件与 Hooks
  2. 02审查 React 19 模式
  3. 03检查无障碍和 TypeScript
  4. 04分析架构与性能
  5. 05按严重程度整理发现与建议

适用场景

  1. 01合并前审查 React 改动
  2. 02检查交互组件的无障碍性
  3. 03发现 Hooks 依赖或清理问题
  4. 04评估状态管理和性能方案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill react-code-review

安装后在 Claude Code 中输入/react-code-review确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
accessibility.md
component-architecture.md
hooks-patterns.md

详细介绍

React 代码审查

概述

此技能为 React 应用提供结构化、全面的代码审查。它会根据 React 19 最佳实践、组件架构模式、Hook 用法、无障碍标准和生产就绪标准评估代码。审查会生成可执行的发现,按严重程度分类(严重、警告、建议),并提供具体的改进代码示例。

通过 Agent 系统调用时,此技能会将深入的架构分析委托给 react-software-architect-review Agent。

适用场景

  • 合并前审查 React 组件、Hook 和页面
  • 验证组件组合与可复用模式
  • 检查 Hook 的正确用法(useState、useEffect、useMemo、useCallback)
  • 审查 React 19 模式(use、useOptimistic、useFormStatus、Actions)
  • 评估状态管理方案(本地状态、Context、外部存储)
  • 评估性能优化(记忆化、代码拆分、延迟加载)
  • 审查无障碍合规性(WCAG、语义化 HTML、ARIA)
  • 验证 Props、状态和事件的 TypeScript 类型
  • 检查 Tailwind CSS 和样式模式
  • 实现新的 React 功能或重构组件架构后

说明

  1. 确定范围:确定要审查哪些 React 组件和 Hook。使用 glob 查找 .tsx/.jsx 文件,并使用 grep 查找组件定义、Hook 用法和 Context 提供者。

  2. 分析组件架构:验证组件组合是否合理——检查是否遵循单一职责、大小是否合适,以及是否可复用。查找过大的组件(>200 行)、Props 过多的组件(>7 个)或职责混杂的组件。

  3. 审查 Hook 用法:验证 Hook 用法是否正确——检查 useEffect/useMemo/useCallback 的依赖数组,验证 useEffect 中的清理函数,并找出因缺少或错误的记忆化而导致的不必要重新渲染。

  4. 评估状态管理:评估状态的存放位置——检查状态是否恰当地就近管理、是否进行了不必要的状态提升,以及 Context 与外部存储的使用是否合适。验证服务端状态是否使用 TanStack Query、SWR 或类似库,而非手动采用 useEffect + useState 模式。

  5. 检查无障碍性:审查语义化 HTML 的使用、ARIA 属性、键盘导航、焦点管理和屏幕阅读器兼容性。验证交互元素是否具备无障碍性,以及表单输入是否有适当的标签。

  6. 评估性能:查找不必要的重新渲染、昂贵组件是否缺少 React.memo、useCallback/useMemo 使用是否不当、是否缺少代码拆分,以及是否导入了过大的包。

  7. 审查 TypeScript 集成:检查 Props 类型定义、事件处理器类型、泛型组件模式,以及工具类型的正确用法。验证在可以使用具体类型时是否避免使用 any。

  8. 生成审查报告:生成结构化报告,包含按严重程度分类的发现(严重、警告、建议)、正面观察,以及附有代码示例的优先级建议。

示例

示例 1:Hook 依赖项问题
// ❌ Bad: Missing dependency causes stale closure
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, []); // Missing userId in dependency array

  return <div>{user?.name}</div>;
}

// ✅ Good: Proper dependencies with cleanup
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    let cancelled = false;
    fetchUser(userId).then((data) => {
      if (!cancelled) setUser(data);
    });
    return () => { cancelled = true; };
  }, [userId]);

  return <div>{user?.name}</div>;
}

// ✅ Better: Use TanStack Query for server state
function UserProfile({ userId }: { userId: string }) {
  const { data: user, isLoading } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  if (isLoading) return <Skeleton />;
  return <div>{user?.name}</div>;
}
示例 2:组件组合
// ❌ Bad: Monolithic component mixing data fetching, filtering, and rendering
function Dashboard() {
  const [users, setUsers] = useState([]);
  const [filter, setFilter] = useState('');
  useEffect(() => { /* fetch + filter + sort all in one */ }, [filter]);
  return <div>{/* 200+ lines of mixed concerns */}</div>;
}

// ✅ Good: Composed from focused components with custom hooks
function Dashboard() {
  return (
    <div>
      <UserFilters />
      <Suspense fallback={<TableSkeleton />}>
        <UserTable />
      </Suspense>
      <UserPagination />
    </div>
  );
}
示例 3:无障碍审查
// ❌ Bad: Inaccessible interactive elements
function Menu({ items }: { items: MenuItem[] }) {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <div onClick={() => setOpen(!open)}>Menu</div>
      {open && (
        <div>
          {items.map(item => (
            <div key={item.id} onClick={() => navigate(item.path)}>
              {item.label}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ✅ Good: Accessible with proper semantics and keyboard support
function Menu({ items }: { items: MenuItem[] }) {
  const [open, setOpen] = useState(false);
  return (
    <nav aria-label="Main navigation">
      <button
        onClick={() => setOpen(!open)}
        aria-expanded={open}
        aria-controls="menu-list"
      >
        Menu
      </button>
      {open && (
        <ul id="menu-list" role="menu">
          {items.map(item => (
            <li key={item.id} role="menuitem">
              <a href={item.path}>{item.label}</a>
            </li>
          ))}
        </ul>
      )}
    </nav>
  );
}
示例 4:性能优化
// ❌ Bad: Unstable callback recreated every render causes child re-renders
{filtered.map(product => (
  <ProductCard
    key={product.id}
    product={product}
    onSelect={() => console.log(product.id)} // New function each render
  />
))}

// ✅ Good: Stable callback + memoized child
const handleSelect = useCallback((id: string) => {
  console.log(id);
}, []);

const filtered = useMemo(
  () => products.filter(p => p.name.toLowerCase().includes(search.toLowerCase())),
  [products, search]
);

{filtered.map(product => (
  <ProductCard key={product.id} product={product} onSelect={handleSelect} />
))}

const ProductCard = memo(function ProductCard({ product, onSelect }: Props) {
  return <div onClick={() => onSelect(product.id)}>{product.name}</div>;
});
示例 5:TypeScript Props 审查
// ❌ Bad: Loose typing and missing prop definitions
function Card({ data, onClick, children, ...rest }: any) {
  return (
    <div onClick={onClick} {...rest}>
      <h2>{data.title}</h2>
      {children}
    </div>
  );
}

// ✅ Good: Strict typing with proper interfaces
interface CardProps extends React.ComponentPropsWithoutRef<'article'> {
  title: string;
  description?: string;
  variant?: 'default' | 'outlined' | 'elevated';
  onAction?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  children: React.ReactNode;
}

function Card({
  title,
  description,
  variant = 'default',
  onAction,
  children,
  className,
  ...rest
}: CardProps) {
  return (
    <article className={cn('card', `card--${variant}`, className)} {...rest}>
      <h2>{title}</h2>
      {description && <p>{description}</p>}
      {children}
      {onAction && <button onClick={onAction}>Action</button>}
    </article>
  );
}

审查输出格式

按以下格式整理所有代码审查发现:

1. 摘要

简要概述整体质量评分(1-10)和关键观察。

2. 严重问题(必须修复)

会导致 Bug、安全漏洞或功能失效的问题。

3. 警告(应当修复)

违反最佳实践、导致性能问题或降低可维护性的问题。

4. 建议(可考虑改进)

在代码组织、无障碍性或开发者体验方面的改进。

5. 正面观察

值得肯定的良好实现模式和实践。

6. 建议

按优先级列出后续步骤,并提供代码示例,帮助实现影响最大的改进。

最佳实践

  • 保持组件职责单一,控制在 200 行以内
  • 将状态与使用它的组件放在一起
  • 使用自定义 Hook 从组件中提取可复用逻辑
  • 只有在测量确认重新渲染成本值得优化时,才使用 React.memo
  • 使用 TanStack Query 或 SWR 管理服务端状态,而不是使用 useEffect + useState
  • 通过 useEffect 订阅外部资源时,务必包含清理函数
  • 优先编写语义化 HTML,仅在原生语义不足时添加 ARIA
  • 启用 TypeScript 严格模式,并避免在组件属性中使用 any
  • 实现错误边界,以便在发生故障时优雅处理
  • 优先使用组合,避免条件渲染过于复杂

限制和警告

  • 遵循项目的 React 版本——不要为较旧版本建议使用 React 19 的特性
  • 除非项目已统一采用某个状态管理库,否则不要强制指定特定库
  • 记忆化并非总有益——只有在重新渲染的影响可以测量时才建议使用
  • 无障碍建议应以 WCAG 2.1 AA 为基准
  • 聚焦于高置信度问题,避免对主观风格选择误报
  • 除非有明确且可衡量的收益,否则不要建议重写正常工作的组件

参考资料

详细的审查清单和模式文档请参阅 references/ 目录:

  • references/hooks-patterns.md — React Hook 最佳实践和常见错误
  • references/component-architecture.md — 组件组合和设计模式
  • references/accessibility.md — React 无障碍检查清单和 ARIA 模式

评论

支持 Markdown