react

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Development Guide

React开发指南

This skill provides comprehensive guidelines, patterns, and best practices for React development in this project.
本指南提供了项目中React开发的全面指导、模式和最佳实践。

Quick Start

快速开始

  1. Best Practices: For component architecture, state management, and TypeScript integration, read
    references/best-practices.md
  2. useEffect Patterns: For understanding when to use (and avoid) useEffect, read
    references/useeffect-patterns.md
  3. Data Fetching: For TanStack Query patterns, use the
    tanstack
    skill
  4. Forms: For form handling with TanStack Form, use the
    tanstack
    skill
  1. 最佳实践:关于组件架构、状态管理和TypeScript集成,请阅读
    references/best-practices.md
  2. useEffect模式:如需了解何时使用(及避免使用)useEffect,请阅读
    references/useeffect-patterns.md
  3. 数据获取:关于TanStack Query模式,请使用
    tanstack
    技能
  4. 表单处理:如需使用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 (
    use()
    , Actions,
    useOptimistic()
    )
  • 仅使用函数式组件:仅使用函数式组件——类组件已属于遗留技术
  • 单一职责:保持组件小巧,专注于单一用途
  • 关注点分离:将行为逻辑提取到自定义Hooks中,让组件专注于渲染
  • 基于功能的组织方式:按功能而非类型来存放相关文件
  • React 19+特性:拥抱现代React特性(
    use()
    、Actions、
    useOptimistic()

Quick Reference Tables

快速参考表格

State Management Hierarchy

状态管理层级

PriorityToolUse Case
1
useState
/
useReducer
Component-specific UI state
2ZustandShared client state across components
3TanStack QueryServer state and data synchronization
4URL stateShareable application state (TanStack Router)
优先级工具使用场景
1
useState
/
useReducer
组件专属UI状态
2Zustand跨组件共享客户端状态
3TanStack Query服务端状态与数据同步
4URL状态可共享的应用状态(TanStack Router)

useEffect Decision Tree

useEffect决策树

SituationDON'TDO
Derived state from props/state
useState
+
useEffect
Calculate during render
Expensive calculations
useEffect
to cache
useMemo
Reset state on prop change
useEffect
with
setState
key
prop
User event responses
useEffect
watching state
Event handler directly
Notify parent of changes
useEffect
calling
onChange
Call in event handler
Fetch data
useEffect
without cleanup
useEffect
with cleanup OR TanStack Query
场景不要做应该做
从props/state派生状态
useState
+
useEffect
在渲染阶段直接计算
昂贵的计算使用
useEffect
缓存
使用
useMemo
当props变化时重置状态
useEffect
中调用
setState
使用
key
属性
响应用户事件监听state的
useEffect
直接使用事件处理程序
通知父组件变化
useEffect
中调用
onChange
在事件处理程序中调用
获取数据无清理逻辑的
useEffect
带清理逻辑的
useEffect
或 TanStack Query

When You DO Need Effects

需要使用Effects的场景

  • Synchronizing with external systems (non-React widgets, browser APIs)
  • Subscriptions to external stores (use
    useSyncExternalStore
    when possible)
  • 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的场景

  1. Transforming data for rendering - Calculate at top level, re-runs automatically
  2. Handling user events - Use event handlers, you know exactly what happened
  3. Deriving state - Just compute it:
    const fullName = firstName + ' ' + lastName
  4. Chaining state updates - Calculate all next state in the event handler
  1. 为渲染转换数据——在渲染阶段直接计算,会自动重新运行
  2. 处理用户事件——使用事件处理程序,你能准确了解发生的操作
  3. 派生状态——直接计算即可:
    const fullName = firstName + ' ' + lastName
  4. 链式状态更新——在事件处理程序中计算所有下一个状态

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:
    useXxx
    pattern
  • 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

文件命名规范

TypePatternExample
Componentskebab-case.tsx
user-avatar.tsx
Hooksuse-kebab-case.ts
use-user-data.ts
UtilitiescamelCase.ts
formatDate.ts
Typestypes.ts
types.ts
Tests*.test.tsx
user-avatar.test.tsx
类型模式示例
组件kebab-case.tsx
user-avatar.tsx
Hooksuse-kebab-case.ts
use-user-data.ts
工具函数camelCase.ts
formatDate.ts
类型定义types.ts
types.ts
测试文件*.test.tsx
user-avatar.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
    ,
    pnpm run typecheck
    , and
    pnpm run test
在完成涉及React的任务之前:
  • 组件为函数式且遵循单一职责原则
  • 行为逻辑已提取到自定义Hooks中
  • TypeScript props已直接定义类型(未使用
    React.FC
  • 状态管理遵循层级结构(本地状态 -> Zustand -> TanStack Query -> URL)
  • useEffect仅用于外部系统同步
  • 已设置错误边界用于错误处理
  • 已处理加载和错误状态
  • 满足可访问性要求(语义化HTML、键盘导航)
  • 已为组件和Hooks编写测试
  • 运行
    pnpm run lint
    pnpm run typecheck
    pnpm run test

Detailed References

详细参考资料

For comprehensive guidance, consult these reference files:
  • references/best-practices.md
    - Component architecture, TypeScript, state management, React 19+ features, testing patterns
  • references/useeffect-patterns.md
    - When to use/avoid useEffect, anti-patterns, and better alternatives
如需全面指导,请查阅以下参考文件:
  • references/best-practices.md
    ——组件架构、TypeScript、状态管理、React 19+特性、测试模式
  • references/useeffect-patterns.md
    ——何时使用/避免useEffect、反模式及更好的替代方案