---
name: react-components-rules
description: "React 组件与 Hooks 开发规范。适用于所有前端 React/Next.js/Vite 项目的代码编写和重构。"
globs: ["src/components/**/*.tsx", "src/components/**/*.ts", "src/hooks/**/*.ts", "src/pages/**/*.tsx", "**/*.tsx", "**/*.jsx"]
alwaysApply: false
updated: 2026-05-22
---

# React 组件与 Hooks 开发规范

> [!IMPORTANT]
> 前端工程的可维护性依赖于清晰的代码结构与严格的生命周期管理。AI 助手在设计或优化 React 组件及自定义 Hooks 时，必须确保组件行数适中、状态可追踪、且没有内存泄漏隐患。

## 1. 适用场景

当您在前端项目中开发新的 React 函数式组件、编写自定义 Hooks，或者维护组件生命周期、注册全局监听器时生效。

## 2. 操作规则

1. **函数式组件优先 (Functional Components First)**：
   - 新组件应优先使用函数式组件；具体声明方式必须遵循项目既有风格（如普通函数、箭头函数或 `React.FC`）。除 Error Boundary 等 React 仍要求 Class 的特殊场景外，不应新增 Class 组件。
2. **严格的 Hooks 规范 (Hook Rules)**：
   - 绝对禁止在 `if` 分支条件句、`for` 循环体内或任何嵌套的常规回调函数中调用 Hook（如 `useState`、`useEffect`）。所有 Hook 调用必须无条件声明在组件或自定义 Hook 的最顶层。
3. **彻底的副作用清理 (Cleanup of Effects)**：
   - 凡是在 `useEffect` 中注册的第三方定时器（`setInterval`/`setTimeout`）、全局 DOM 事件监听器（如 `window.addEventListener`）或 WebSocket 订阅，**必须在 `useEffect` 的 Return 清理函数中无条件卸载和销毁**，严防内存泄漏与重复绑定。
4. **业务与 UI 解耦 (Logic & UI Separation)**：
   - 当组件的代码行数**超过 200 行**时，AI 应当自动将复杂的纯逻辑计算部分剥离到独立的自定义 Hook（如 `useComponentLogic.ts`）中，保持 UI 组件的职责纯粹与轻量。

## 3. 禁止事项

- 严禁在 React 组件中直接操作 DOM（如裸用 `document.getElementById`），所有 DOM 交互必须使用 `useRef`。
- 严禁为了防止依赖报警而在 `useEffect` 的依赖数组中故意省略必要的变量依赖。所有依赖参数必须通过合理使用 `useCallback` / `useMemo` 进行包裹优化，并交由 ESLint `react-hooks/exhaustive-deps` 严格查杀。

## 4. 验证方式

- 运行 `npm run lint` 或 `eslint . --ext .ts,.tsx`。

## 5. 代码对比示例

### ❌ 错误示例（内存泄漏、缺少清理、乱操作 DOM）

```tsx
// 违反规则 2.3：直接在 Effect 中使用了全局 addEventListener，但完全没有在卸载时进行销毁
// 违反规则 2.1：在非 Error Boundary 场景新增 Class 组件，代码臃肿
import React from 'react';

export class OldWindowTracker extends React.Component {
  componentDidMount() {
    window.addEventListener('resize', () => {
      // 缺点：由于是一个匿名函数，在组件销毁时很难对其进行移除销毁，造成内存泄漏
      console.log(window.innerWidth);
    });
  }

  render() {
    return <div>请改变窗口大小查看日志</div>;
  }
}
```

###  正确方向（函数式组件、副作用完美清理、自定义 Hooks 抽象）

```tsx
import { useState, useEffect } from "react";

// 遵循规则 2.4：将逻辑抽取到独立的自定义 Hook 中，可复用且易测试
export function useWindowWidth(): number {
  const [width, setWidth] = useState<number>(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    // 遵循规则 2.3：添加全局监听
    window.addEventListener("resize", handleResize);

    // 遵循规则 2.3：在组件销毁卸载时进行无条件解绑清理，防内存泄漏
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []); // 空依赖数组，确保仅在挂载与卸载时执行一次

  return width;
}

// 遵循规则 2.1：优雅纯粹的函数式组件，职责单一，没有 UI 与逻辑污染
export const ResponsiveTracker: React.FC = () => {
  const width = useWindowWidth();

  return (
    <div className="p-4 text-center text-gray-700 bg-gray-100 rounded-lg">
      <p>当前视口宽度为: <span className="font-bold text-blue-600">{width}px</span></p>
    </div>
  );
};
```
