关于 react
react 是面向 React 应用开发者的参考技能,涵盖组件与 Hooks 模式、性能、无障碍、安全、测试和故障排查。
主要功能
- 01函数组件与 Props 模式
- 02常用 React Hooks
- 03状态与组合指导
- 04性能优化
- 05无障碍与安全实践
- 06测试与故障排查指导
适用场景
- 01组织可复用的 React 组件
- 02管理本地与共享状态
- 03诊断 Hooks 和渲染问题
- 04检查无障碍与生产就绪情况
MCP 相关
README 指向 mcp__documentation__fetch_docs,并使用 technology `react` 获取完整的 React 文档。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill react安装后在 Claude Code 中输入/react确认已加载。
附带文件quick-ref/component-patterns.mdquick-ref/hooks-cheatsheet.md
详细介绍
React Core 知识
深入了解:使用
mcp__documentation__fetch_docs并指定技术:react,获取有关 hooks、组件和 React 模式的全面文档。
组件模式
函数组件(首选)
function UserCard({ name, email }: UserCardProps) {
return (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
Props 类型
interface Props {
title: string;
count?: number;
children: React.ReactNode;
onClick: (id: string) => void;
}
必备 Hooks
| Hook | 用途 |
|---|---|
useState | 本地状态 |
useEffect | 副作用、订阅 |
useContext | 访问上下文值 |
useRef | DOM 引用、可变值 |
useMemo | 缓存开销较大的计算 |
useCallback | 缓存函数 |
useReducer | 复杂状态逻辑 |
关键模式
- 组合优于继承
- 共享状态时向上提升状态
- Props 层层传递 → 使用 Context 或状态库
- 受控与非受控输入
性能
- 对开销较大的纯组件使用
React.memo() - 仅在需要时使用
useMemo/useCallback进行缓存 - 在列表中正确使用
key属性 - 使用
React.lazy()+Suspense延迟加载
生产就绪
安全最佳实践
// NEVER use dangerouslySetInnerHTML with user input
// BAD
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// GOOD - Sanitize with DOMPurify
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
// Avoid exposing sensitive data in client-side state
// BAD
const [apiKey, setApiKey] = useState(process.env.API_KEY);
// GOOD - API keys should stay server-side
// Use API routes or server actions instead
// Validate all external URLs
const isValidUrl = (url: string) => {
try {
const parsed = new URL(url);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
};
错误边界
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
// Usage
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => window.location.reload()}
onError={(error, info) => {
// Log to error reporting service
logErrorToService(error, info);
}}
>
<App />
</ErrorBoundary>
性能优化
// Code splitting with lazy loading
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<Dashboard />
</Suspense>
);
}
// Memoization - use sparingly, only for expensive components
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
return items.map(item => <ExpensiveItem key={item.id} {...item} />);
});
// Virtualization for large lists
import { FixedSizeList } from 'react-window';
function VirtualList({ items }: { items: Item[] }) {
return (
<FixedSizeList
height={400}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
);
}
无障碍(a11y)
// Use semantic HTML
<button onClick={handleClick}>Submit</button> // NOT <div onClick>
// ARIA labels for icons/images
<button aria-label="Close dialog" onClick={onClose}>
<XIcon />
</button>
// Focus management
const dialogRef = useRef<HTMLDivElement>(null);
useEffect(() => {
dialogRef.current?.focus();
}, [isOpen]);
// Keyboard navigation
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
if (e.key === 'Tab') trapFocus(e);
};
测试配置
// Component testing with Testing Library
import { render, screen, userEvent } from '@testing-library/react';
test('submits form with user data', async () => {
const onSubmit = vi.fn();
render(<Form onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText(/email/i), '[email protected]');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(onSubmit).toHaveBeenCalledWith({ email: '[email protected]' });
});
监控指标
| 指标 | 告警阈值 |
|---|---|
| 最大内容绘制(LCP) | > 2.5s |
| 首次输入延迟(FID) | > 100ms |
| 累积布局偏移(CLS) | > 0.1 |
| JavaScript 包大小 | > 200KB(gzip 压缩后) |
| 组件渲染时间 | > 16ms |
构建与包优化
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown'],
},
},
},
sourcemap: true, // For error tracking in production
},
});
检查清单
- 为关键区域添加错误边界
- 客户端状态中不包含敏感数据
- 渲染任何 HTML 时使用 DOMPurify
- 为基于路由的代码拆分启用延迟加载
- 对大型列表(>100 项)使用虚拟化
- 使用语义化 HTML 和 ARIA 标签
- 支持键盘导航
- 监控 Core Web Vitals
- 优化包大小(<200KB gzip)
- 提供用于生产环境调试的源映射
- 集成错误报告服务
不应使用此技能的情况
- React 19 专属功能(Actions、useActionState、use())——改用
react-19技能 - 高级性能优化——改用
react-performance技能 - 表单处理模式——改用
react-forms或react-hook-form技能 - 路由——改用
react-router技能 - 测试——改用
react-testing技能 - 组件设计模式——改用
react-patterns技能
反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 直接修改状态 | 不会触发重新渲染 | 使用 setState 并传入新的对象/数组 |
| 缺少依赖数组 | 闭包过期、内存泄漏 | 包含所有依赖项或使用 ESLint |
| 使用 useEffect 派生状态 | 额外的重新渲染 | 在渲染期间计算或使用 useMemo |
| Props 深层层层传递 | 难以维护 | 使用 Context 或状态库 |
| 在 JSX 中内联对象/数组 | 会破坏缓存 | 提取为常量或使用 useMemo |
| 在渲染期间创建新函数 | 导致子组件不必要地重新渲染 | 使用 useCallback |
| 忘记在 useEffect 中清理 | 内存泄漏 | 返回清理函数 |
| 使用索引作为 key | 重新渲染不正确 | 使用稳定的唯一 ID |
快速故障排查
| 问题 | 可能原因 | 修复方法 |
|---|---|---|
| 组件未重新渲染 | 状态被修改 | 使用 setState 并传入新的引用 |
| useEffect 中出现无限循环 | 缺少/错误的依赖项 | 添加依赖项或使用函数式更新 |
| “Cannot read property of undefined” | 异步数据尚未加载 | 添加空值检查或加载状态 |
| 内存泄漏警告 | 缺少清理 | 从 useEffect 返回清理函数 |
| 子组件未更新 | 使用索引作为 key | 使用稳定的唯一 ID |
| 处理函数未触发 | 事件传播已停止 | 检查 stopPropagation() 调用 |
| 回调中的状态过期 | 闭包引用了旧状态 | 使用函数式 setState |
Reference Documentation
评论
支持 Markdown