react
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Development Guide
React开发指南
This skill provides comprehensive guidelines, patterns, and best practices for React development in this project.
本指南提供了项目中React开发的全面指导、模式和最佳实践。
Quick Start
快速开始
- Best Practices: For component architecture, state management, and TypeScript integration, read
references/best-practices.md - useEffect Patterns: For understanding when to use (and avoid) useEffect, read
references/useeffect-patterns.md - Data Fetching: For TanStack Query patterns, use the skill
tanstack - Forms: For form handling with TanStack Form, use the skill
tanstack
- 最佳实践:关于组件架构、状态管理和TypeScript集成,请阅读
references/best-practices.md - useEffect模式:如需了解何时使用(及避免使用)useEffect,请阅读
references/useeffect-patterns.md - 数据获取:关于TanStack Query模式,请使用技能
tanstack - 表单处理:如需使用TanStack Form处理表单,请使用技能
tanstack
Core Principles
核心原则
- Functional Components Only: Use functional components exclusively - class components are legacy
- Single Responsibility: Keep components small and focused on a single purpose
- Separation of Concerns: Extract behavior logic into custom hooks, keep components focused on rendering
- Feature-Based Organization: Co-locate related files by feature, not by type
- React 19+ Features: Embrace modern React features (, Actions,
use())useOptimistic()
- 仅使用函数式组件:仅使用函数式组件——类组件已属于遗留技术
- 单一职责:保持组件小巧,专注于单一用途
- 关注点分离:将行为逻辑提取到自定义Hooks中,让组件专注于渲染
- 基于功能的组织方式:按功能而非类型来存放相关文件
- React 19+特性:拥抱现代React特性(、Actions、
use())useOptimistic()
Quick Reference Tables
快速参考表格
State Management Hierarchy
状态管理层级
| Priority | Tool | Use Case |
|---|---|---|
| 1 | | Component-specific UI state |
| 2 | Zustand | Shared client state across components |
| 3 | TanStack Query | Server state and data synchronization |
| 4 | URL state | Shareable application state (TanStack Router) |
| 优先级 | 工具 | 使用场景 |
|---|---|---|
| 1 | | 组件专属UI状态 |
| 2 | Zustand | 跨组件共享客户端状态 |
| 3 | TanStack Query | 服务端状态与数据同步 |
| 4 | URL状态 | 可共享的应用状态(TanStack Router) |
useEffect Decision Tree
useEffect决策树
| Situation | DON'T | DO |
|---|---|---|
| Derived state from props/state | | Calculate during render |
| Expensive calculations | | |
| Reset state on prop change | | |
| User event responses | | Event handler directly |
| Notify parent of changes | | Call in event handler |
| Fetch data | | |
| 场景 | 不要做 | 应该做 |
|---|---|---|
| 从props/state派生状态 | | 在渲染阶段直接计算 |
| 昂贵的计算 | 使用 | 使用 |
| 当props变化时重置状态 | | 使用 |
| 响应用户事件 | 监听state的 | 直接使用事件处理程序 |
| 通知父组件变化 | | 在事件处理程序中调用 |
| 获取数据 | 无清理逻辑的 | 带清理逻辑的 |
When You DO Need Effects
需要使用Effects的场景
- Synchronizing with external systems (non-React widgets, browser APIs)
- Subscriptions to external stores (use when possible)
useSyncExternalStore - Analytics/logging that runs because component displayed
- Data fetching with proper cleanup (or use TanStack Query)
- 与外部系统同步(非React组件、浏览器API)
- 订阅外部存储(尽可能使用)
useSyncExternalStore - 因组件渲染而触发的分析/日志记录
- 带有适当清理逻辑的数据获取(或使用TanStack Query)
When You DON'T Need Effects
不需要使用Effects的场景
- Transforming data for rendering - Calculate at top level, re-runs automatically
- Handling user events - Use event handlers, you know exactly what happened
- Deriving state - Just compute it:
const fullName = firstName + ' ' + lastName - Chaining state updates - Calculate all next state in the event handler
- 为渲染转换数据——在渲染阶段直接计算,会自动重新运行
- 处理用户事件——使用事件处理程序,你能准确了解发生的操作
- 派生状态——直接计算即可:
const fullName = firstName + ' ' + lastName - 链式状态更新——在事件处理程序中计算所有下一个状态
TypeScript Integration
TypeScript集成
typescript
// CORRECT: Type props directly (never use React.FC)
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
function Button({ variant, children }: ButtonProps) {
return <button className={variant}>{children}</button>;
}
// Extending HTML elements
interface InputProps extends React.ComponentProps<"input"> {
label: string;
}typescript
// 正确做法:直接为props定义类型(切勿使用React.FC)
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
function Button({ variant, children }: ButtonProps) {
return <button className={variant}>{children}</button>;
}
// 扩展HTML元素
interface InputProps extends React.ComponentProps<"input"> {
label: string;
}Custom Hooks Guidelines
自定义Hooks指南
- Extract non-visual logic into custom hooks
- Keep hooks focused on single purpose
- Use clear naming: pattern
useXxx - Return arrays for state-like hooks, objects for complex returns
typescript
// State-like hook returns array
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const;
}
// Complex hook returns object
function useUser(id: string) {
const query = useQuery({ queryKey: ["user", id], queryFn: () => fetchUser(id) });
return {
user: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
};
}- 将非视觉逻辑提取到自定义Hooks中
- 保持Hooks专注于单一用途
- 使用清晰的命名:模式
useXxx - 类状态的Hooks返回数组,复杂返回使用对象
typescript
// 类状态的Hook返回数组
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const;
}
// 复杂Hook返回对象
function useUser(id: string) {
const query = useQuery({ queryKey: ["user", id], queryFn: () => fetchUser(id) });
return {
user: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
};
}Component Architecture Pattern
组件架构模式
typescript
// CORRECT: Hook handles all logic, component handles rendering
function useIssueSearch(projectId: string) {
const [query, setQuery] = useState("");
const [filters, setFilters] = useState<Filters>({});
const issues = useQuery({
queryKey: ["issues", projectId, query, filters],
queryFn: () => searchIssues(projectId, query, filters),
});
return {
query,
setQuery,
filters,
setFilters,
issues: issues.data ?? [],
isLoading: issues.isLoading,
};
}
function IssueList({ projectId }: { projectId: string }) {
const { query, setQuery, issues, isLoading } = useIssueSearch(projectId);
return (
<div>
<SearchInput value={query} onChange={setQuery} />
{isLoading ? <Loading /> : <IssueTable issues={issues} />}
</div>
);
}typescript
// 正确做法:Hook处理所有逻辑,组件负责渲染
function useIssueSearch(projectId: string) {
const [query, setQuery] = useState("");
const [filters, setFilters] = useState<Filters>({});
const issues = useQuery({
queryKey: ["issues", projectId, query, filters],
queryFn: () => searchIssues(projectId, query, filters),
});
return {
query,
setQuery,
filters,
setFilters,
issues: issues.data ?? [],
isLoading: issues.isLoading,
};
}
function IssueList({ projectId }: { projectId: string }) {
const { query, setQuery, issues, isLoading } = useIssueSearch(projectId);
return (
<div>
<SearchInput value={query} onChange={setQuery} />
{isLoading ? <Loading /> : <IssueTable issues={issues} />}
</div>
);
}File Naming Conventions
文件命名规范
| Type | Pattern | Example |
|---|---|---|
| Components | kebab-case.tsx | |
| Hooks | use-kebab-case.ts | |
| Utilities | camelCase.ts | |
| Types | types.ts | |
| Tests | *.test.tsx | |
| 类型 | 模式 | 示例 |
|---|---|---|
| 组件 | kebab-case.tsx | |
| Hooks | use-kebab-case.ts | |
| 工具函数 | camelCase.ts | |
| 类型定义 | types.ts | |
| 测试文件 | *.test.tsx | |
Testing with Vitest
使用Vitest进行测试
typescript
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { renderHook, act } from "@testing-library/react";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByText("Hello")).toBeInTheDocument();
});
});
describe("useMyHook", () => {
it("returns expected value", () => {
const { result } = renderHook(() => useMyHook());
expect(result.current.value).toBe(expected);
});
});typescript
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { renderHook, act } from "@testing-library/react";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByText("Hello")).toBeInTheDocument();
});
});
describe("useMyHook", () => {
it("returns expected value", () => {
const { result } = renderHook(() => useMyHook());
expect(result.current.value).toBe(expected);
});
});Validation Checklist
验证检查清单
Before finishing a task involving React:
- Components are functional and follow single responsibility principle
- Behavior logic is extracted into custom hooks
- TypeScript props are typed directly (not using )
React.FC - State management follows the hierarchy (local -> Zustand -> TanStack Query -> URL)
- useEffect is only used for external system synchronization
- Error boundaries are in place for error handling
- Loading and error states are handled
- Accessibility requirements are met (semantic HTML, keyboard navigation)
- Tests are written for components and hooks
- Run ,
pnpm run lint, andpnpm run typecheckpnpm run test
在完成涉及React的任务之前:
- 组件为函数式且遵循单一职责原则
- 行为逻辑已提取到自定义Hooks中
- TypeScript props已直接定义类型(未使用)
React.FC - 状态管理遵循层级结构(本地状态 -> Zustand -> TanStack Query -> URL)
- useEffect仅用于外部系统同步
- 已设置错误边界用于错误处理
- 已处理加载和错误状态
- 满足可访问性要求(语义化HTML、键盘导航)
- 已为组件和Hooks编写测试
- 运行、
pnpm run lint和pnpm run typecheckpnpm run test
Detailed References
详细参考资料
For comprehensive guidance, consult these reference files:
- - Component architecture, TypeScript, state management, React 19+ features, testing patterns
references/best-practices.md - - When to use/avoid useEffect, anti-patterns, and better alternatives
references/useeffect-patterns.md
如需全面指导,请查阅以下参考文件:
- ——组件架构、TypeScript、状态管理、React 19+特性、测试模式
references/best-practices.md - ——何时使用/避免useEffect、反模式及更好的替代方案
references/useeffect-patterns.md