web-framework-react

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Components

React 组件

Quick Guide: Tiered components (Primitives -> Components -> Patterns -> Templates). React 19: pass
ref
as a prop directly (no
forwardRef
needed). Expose
className
prop for styling flexibility. Use
useActionState
for forms,
useOptimistic
for instant feedback,
use()
for conditional promise/context reading. Ref callbacks can return cleanup functions.

<critical_requirements>
快速指南: 分层组件(基础组件 -> 通用组件 -> 设计模式 -> 页面模板)。React 19:直接将
ref
作为属性传递(无需使用
forwardRef
)。暴露
className
属性以提升样式灵活性。使用
useActionState
处理表单,
useOptimistic
实现即时反馈,
use()
用于条件式读取Promise/上下文。Ref回调可返回清理函数。

<critical_requirements>

CRITICAL: Before Using This Skill

重要须知:使用本技能前需了解

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST pass
ref
as a regular prop in React 19 -
forwardRef
is deprecated)
(You MUST expose
className
prop on ALL reusable components for customization)
(You MUST use
useActionState
for form submissions with pending/error state)
(You MUST call
useFormStatus
from a child component inside
<form>
, NOT in the component that renders the form)
</critical_requirements>

Auto-detection: React 19, components, hooks, use(), useActionState, useFormStatus, useOptimistic, Actions, ref as prop, ref cleanup, forwardRef migration, component variants, error boundary
When to use:
  • Building React components with type-safe props
  • Migrating from forwardRef to React 19 ref-as-prop
  • Handling form submissions with React 19 Actions API
  • Creating custom hooks for reusable logic
  • Implementing error boundaries with retry
When NOT to use:
  • Simple one-off components without variants (skip variant abstractions)
  • Static content without interactivity
Key patterns covered:
  • Component architecture tiers and variant props
  • React 19 ref as prop (replaces forwardRef)
  • React 19 hooks:
    use()
    ,
    useActionState
    ,
    useFormStatus
    ,
    useOptimistic
  • Ref callback cleanup functions
  • Error boundaries with retry and custom fallbacks
  • Custom hooks (pagination, debounce, localStorage)
  • Event handler naming conventions

<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名法、命名导出、导入顺序、
import type
、命名常量)
(在React 19中必须将
ref
作为常规属性传递——
forwardRef
已被弃用)
(所有可复用组件必须暴露
className
属性以支持自定义样式)
(处理带有等待/错误状态的表单提交时必须使用
useActionState
)
(必须在
<form>
内部的子组件中调用
useFormStatus
,而非渲染表单的组件本身)
</critical_requirements>

自动检测: React 19、组件、Hooks、use()、useActionState、useFormStatus、useOptimistic、Actions、ref作为属性、ref清理、forwardRef迁移、组件变体、错误边界
适用场景:
  • 构建带有类型安全属性的React组件
  • 从forwardRef迁移至React 19的ref-as-prop写法
  • 使用React 19 Actions API处理表单提交
  • 创建用于复用逻辑的自定义Hooks
  • 实现带重试机制的错误边界
不适用场景:
  • 无变体的简单一次性组件(跳过变体抽象)
  • 无交互性的静态内容
涵盖的核心模式:
  • 组件架构分层与变体属性
  • React 19的ref作为属性(替代forwardRef)
  • React 19 Hooks:
    use()
    、
    useActionState
    、
    useFormStatus
    、
    useOptimistic
  • Ref回调清理函数
  • 带重试和自定义回退的错误边界
  • 自定义Hooks(分页、防抖、本地存储)
  • 事件处理器命名规范

<philosophy>

Philosophy

设计理念

React components follow a tiered architecture from low-level primitives to high-level templates. Components should be composable, type-safe, and expose necessary customization points (
className
, refs). Use variant abstractions only when components have multiple variant dimensions to avoid over-engineering. React is styling-agnostic -- apply styles via the
className
prop.
React 19 Changes:
forwardRef
is deprecated -- pass
ref
as a regular prop directly. New hooks (
use()
,
useActionState
,
useFormStatus
,
useOptimistic
) simplify data fetching and form handling with the Actions API. Ref callbacks can return cleanup functions, eliminating the need for separate
useEffect
cleanup.
</philosophy>
<patterns>
React组件遵循从底层基础组件到高层页面模板的分层架构。组件应具备可组合性、类型安全性,并暴露必要的自定义点(
className
、refs)。仅当组件存在多个变体维度时才使用变体抽象,避免过度设计。React与样式方案无关——通过
className
属性应用样式。
React 19变更:
forwardRef
已被弃用——直接将
ref
作为常规属性传递。新Hooks(
use()
、
useActionState
、
useFormStatus
、
useOptimistic
)结合Actions API简化了数据获取和表单处理。Ref回调可返回清理函数,无需单独使用
useEffect
进行清理。
</philosophy>
<patterns>

Core Patterns

核心模式

Pattern 1: Component Architecture Tiers

模式1:组件架构分层

Components are organized in a tiered hierarchy:
  1. Primitives (
    src/primitives/
    ) - Low-level building blocks (skeleton)
  2. Components (
    src/components/
    ) - Reusable UI (button, switch, select)
  3. Patterns (
    src/patterns/
    ) - Composed patterns (feature, navigation)
  4. Templates (
    src/templates/
    ) - Page layouts (frame)
typescript
// React 19: ref as a regular prop, no forwardRef needed
export type ButtonProps = React.ComponentProps<"button"> & {
  variant?: "default" | "ghost" | "link";
  size?: "default" | "large" | "icon";
  asChild?: boolean;
  ref?: React.Ref<HTMLButtonElement>;
};

export function Button({ variant = "default", size = "default", className, ref, ...props }: ButtonProps) {
  return <button className={className} data-variant={variant} data-size={size} ref={ref} {...props} />;
}
Why good: ref as regular prop eliminates forwardRef boilerplate, className enables external styling, data-attributes enable CSS selectors for variants
See examples/core.md for complete component examples with good/bad comparisons.

组件按层级结构组织:
  1. 基础组件(
    src/primitives/
    )- 底层构建块(骨架)
  2. 通用组件(
    src/components/
    )- 可复用UI(按钮、开关、选择器)
  3. 设计模式(
    src/patterns/
    )- 组合式模式(功能模块、导航)
  4. 页面模板(
    src/templates/
    )- 页面布局(框架)
typescript
// React 19: ref作为常规属性,无需forwardRef
export type ButtonProps = React.ComponentProps<"button"> & {
  variant?: "default" | "ghost" | "link";
  size?: "default" | "large" | "icon";
  asChild?: boolean;
  ref?: React.Ref<HTMLButtonElement>;
};

export function Button({ variant = "default", size = "default", className, ref, ...props }: ButtonProps) {
  return <button className={className} data-variant={variant} data-size={size} ref={ref} {...props} />;
}
优势: ref作为常规属性消除了forwardRef的样板代码,className支持外部样式定制,data属性允许CSS选择器定位变体
查看examples/core.md获取包含优劣对比的完整组件示例。

Pattern 2: Component Variant Props

模式2:组件变体属性

Components with 2+ visual dimensions (variant, size) should expose type-safe variant props via TypeScript unions. Use
data-*
attributes so any styling solution can target them.
typescript
export type AlertVariant = "info" | "warning" | "error" | "success";

export function Alert({ variant = "info", className, ref, ...props }: AlertProps) {
  return <div ref={ref} className={className} data-variant={variant} {...props} />;
}
When not to use: Components with a single visual style -- skip variant abstraction.
See examples/core.md for variant props with good/bad examples.

拥有2个及以上视觉维度(变体、尺寸)的组件应通过TypeScript联合类型暴露类型安全的变体属性。使用
data-*
属性以便任何样式方案都能定位它们。
typescript
export type AlertVariant = "info" | "warning" | "error" | "success";

export function Alert({ variant = "info", className, ref, ...props }: AlertProps) {
  return <div ref={ref} className={className} data-variant={variant} {...props} />;
}
不适用场景: 仅有一种视觉样式的组件——跳过变体抽象。
查看examples/core.md获取变体属性的优劣示例。

Pattern 3: Event Handler Naming

模式3:事件处理器命名

  • handle
    prefix for internal handlers:
    handleSubmit
    ,
    handleNameChange
  • on
    prefix for callback props:
    onClick
    ,
    onSubmit
  • Type events explicitly:
    FormEvent<HTMLFormElement>
    ,
    ChangeEvent<HTMLInputElement>
typescript
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
  setName(e.target.value);
};
See examples/core.md for full event handler examples.

  • 内部处理器使用
    handle
    前缀:
    handleSubmit
    、
    handleNameChange
  • 回调属性使用
    on
    前缀:
    onClick
    、
    onSubmit
  • 显式指定事件类型:
    FormEvent<HTMLFormElement>
    、
    ChangeEvent<HTMLInputElement>
typescript
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
  setName(e.target.value);
};
查看examples/core.md获取完整的事件处理器示例。

Pattern 4: Custom Hooks

模式4:自定义Hooks

Extract reusable logic into custom hooks following the
use
prefix convention.
  • usePagination
    - Pagination state and navigation
  • useDebounce
    - Debounce values for search inputs
  • useLocalStorage
    - Type-safe localStorage persistence with SSR safety
See examples/hooks.md for complete implementations.

遵循
use
前缀约定,将可复用逻辑提取为自定义Hooks。
  • usePagination
    - 分页状态与导航
  • useDebounce
    - 搜索输入值防抖
  • useLocalStorage
    - 支持SSR安全的类型安全本地存储持久化
查看examples/hooks.md获取完整实现。

Pattern 5: Error Boundaries with Retry

模式5:带重试机制的错误边界

Error boundaries catch render errors and provide retry capability. Place them around feature sections, not just the root.
typescript
// Key interface -- accepts custom fallback and error callback
interface Props {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
Limitation: Error boundaries do not catch event handler errors, async errors, or SSR errors -- use try/catch for those.
See examples/error-boundaries.md for full implementation.

错误边界捕获渲染错误并提供重试功能。将其放置在功能模块周围,而非仅在根节点。
typescript
// 核心接口——接受自定义回退和错误回调
interface Props {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
局限性: 错误边界无法捕获事件处理器错误、异步错误或SSR错误——这些需使用try/catch处理。
查看examples/error-boundaries.md获取完整实现。

Pattern 6: useActionState for Form Submissions

模式6:使用useActionState处理表单提交

Skip if your framework provides its own server-side form handling (Server Actions) — use that instead.
Use
useActionState
for form submissions with automatic pending state and error handling. Replaces manual
useState
for loading/error.
typescript
import { useActionState } from "react";

async function updateProfile(prevState: string | null, formData: FormData) {
  try {
    await saveProfile({ name: formData.get("name") as string });
    return null;
  } catch {
    return "Failed to save profile";
  }
}

export function ProfileForm() {
  const [error, submitAction, isPending] = useActionState(updateProfile, null);

  return (
    <form action={submitAction}>
      <input type="text" name="name" disabled={isPending} />
      <button type="submit" disabled={isPending}>
        {isPending ? "Saving..." : "Save"}
      </button>
      {error && <p role="alert">{error}</p>}
    </form>
  );
}
Why good: hook manages pending and error state automatically, form action works with progressive enhancement, no manual useState for loading/error
See examples/react-19-hooks.md for extended examples with success state.

如果你的框架提供了自身的服务端表单处理(Server Actions),请使用该方案——跳过此部分。
使用
useActionState
处理表单提交,自动管理等待状态和错误处理。替代手动使用
useState
管理加载/错误状态。
typescript
import { useActionState } from "react";

async function updateProfile(prevState: string | null, formData: FormData) {
  try {
    await saveProfile({ name: formData.get("name") as string });
    return null;
  } catch {
    return "Failed to save profile";
  }
}

export function ProfileForm() {
  const [error, submitAction, isPending] = useActionState(updateProfile, null);

  return (
    <form action={submitAction}>
      <input type="text" name="name" disabled={isPending} />
      <button type="submit" disabled={isPending}>
        {isPending ? "Saving..." : "Save"}
      </button>
      {error && <p role="alert">{error}</p>}
    </form>
  );
}
优势: Hook自动管理等待和错误状态,表单操作支持渐进式增强,无需手动使用useState管理加载/错误状态
查看examples/react-19-hooks.md获取包含成功状态的扩展示例。

Pattern 7: useFormStatus for Submit Buttons

模式7:使用useFormStatus处理提交按钮

useFormStatus
reads parent form's pending state without prop drilling. Must be called from a child component inside the
<form>
.
typescript
import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}
Gotcha: Calling
useFormStatus
in the component that renders
<form>
returns
pending: false
always -- it must be a descendant component.
See examples/react-19-hooks.md for reusable submit button patterns.

useFormStatus
读取父表单的等待状态,无需属性透传。必须在
<form>
内部的子组件中调用。
typescript
import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}
注意事项: 在渲染
<form>
的组件中调用
useFormStatus
将始终返回
pending: false
——必须在后代组件中调用。
查看examples/react-19-hooks.md获取可复用提交按钮模式。

Pattern 8: useOptimistic for Instant UI Feedback

模式8:使用useOptimistic实现即时UI反馈

Show immediate UI updates while async operations complete. State automatically reverts if the request fails.
typescript
import { useOptimistic, startTransition } from "react";

const [optimisticItems, addOptimistic] = useOptimistic(
  items,
  (state, update: Item) => [...state, { ...update, pending: true }],
);

// In handler -- setter MUST be called inside startTransition:
startTransition(async () => {
  addOptimistic(newItem);
  await saveItem(newItem);
});
See examples/react-19-hooks.md for todo list and chat examples.

在异步操作完成时显示即时UI更新。如果请求失败,状态将自动回滚。
typescript
import { useOptimistic, startTransition } from "react";

const [optimisticItems, addOptimistic] = useOptimistic(
  items,
  (state, update: Item) => [...state, { ...update, pending: true }],
);

// 在处理器中——必须在startTransition内调用setter:
startTransition(async () => {
  addOptimistic(newItem);
  await saveItem(newItem);
});
查看examples/react-19-hooks.md获取待办事项和聊天示例。

Pattern 9: use() Hook for Promises and Context

模式9:使用use() Hook处理Promise和上下文

use()
reads promises and context conditionally in render -- unlike
useContext
, it can be called after early returns.
typescript
import { use, Suspense } from "react";

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise); // Suspends until resolved
  return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>;
}

// Wrap in Suspense boundary
<Suspense fallback={<p>Loading...</p>}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>
Gotcha:
use()
cannot be called in try-catch blocks -- use Error Boundaries for rejected promise handling.
See examples/react-19-hooks.md for conditional context reading.

use()
在渲染时条件式读取Promise和上下文——与
useContext
不同,它可以在提前返回后调用。
typescript
import { use, Suspense } from "react";

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise); // 挂起直到解析完成
  return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>;
}

// 包裹在Suspense边界中
<Suspense fallback={<p>Loading...</p>}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>
注意事项:
use()
不能在try-catch块中调用——使用错误边界处理Promise拒绝。
查看examples/react-19-hooks.md获取条件式上下文读取示例。

Pattern 10: Ref Callback Cleanup Functions

模式10:Ref回调清理函数

React 19 ref callbacks can return cleanup functions, replacing the need for separate
useEffect
cleanup.
typescript
function VideoPlayer({ src }: { src: string }) {
  return (
    <video
      ref={(video) => {
        if (!video) return;
        video.play();
        return () => {
          video.pause();
          video.currentTime = 0;
        };
      }}
      src={src}
    />
  );
}
Why good: cleanup runs automatically on unmount, no separate useEffect needed, simpler than useRef + useEffect combination
Note: With ref cleanup functions, TypeScript rejects non-null/undefined return values from ref callbacks. The callback is no longer called with
null
on unmount -- the cleanup function handles that.
See examples/react-19-hooks.md for IntersectionObserver cleanup example.
</patterns>
Detailed Resources:
  • examples/core.md - Component architecture, variants, event handlers
  • examples/hooks.md - usePagination, useDebounce, useLocalStorage
  • examples/react-19-hooks.md - useActionState, useFormStatus, useOptimistic, use(), ref cleanup
  • examples/error-boundaries.md - Error boundary implementation and recovery
  • reference.md - Decision frameworks, anti-patterns, checklists

<red_flags>
React 19的Ref回调可返回清理函数,替代单独使用
useEffect
进行清理的需求。
typescript
function VideoPlayer({ src }: { src: string }) {
  return (
    <video
      ref={(video) => {
        if (!video) return;
        video.play();
        return () => {
          video.pause();
          video.currentTime = 0;
        };
      }}
      src={src}
    />
  );
}
优势: 清理函数在卸载时自动执行,无需单独的useEffect,比useRef+useEffect组合更简洁
注意: 使用Ref清理函数时,TypeScript会拒绝Ref回调返回非null/undefined的值。回调在卸载时不再以
null
调用——清理函数会处理该情况。
查看examples/react-19-hooks.md获取IntersectionObserver清理示例。
</patterns>
详细资源:
  • examples/core.md - 组件架构、变体、事件处理器
  • examples/hooks.md - usePagination、useDebounce、useLocalStorage
  • examples/react-19-hooks.md - useActionState、useFormStatus、useOptimistic、use()、ref清理
  • examples/error-boundaries.md - 错误边界实现与恢复
  • reference.md - 决策框架、反模式、检查清单

<red_flags>

RED FLAGS

注意事项

High Priority Issues:
  • Using
    forwardRef
    in React 19 -- deprecated, pass
    ref
    as a regular prop
  • Calling
    useFormStatus
    in the component that renders
    <form>
    -- will always return
    pending: false
  • Using
    useState
    for form loading/error when
    useActionState
    exists -- unnecessary boilerplate
  • Not exposing
    className
    prop on reusable components -- prevents external styling
  • Calling
    use()
    inside try-catch blocks -- use Error Boundaries instead
Medium Priority Issues:
  • Adding variant abstractions for components without multiple variant dimensions
  • Using
    useCallback
    on every handler regardless of child memoization (premature optimization)
  • Wrapping ref callbacks in
    useCallback
    when the React Compiler handles memoization automatically
  • Generic event handler names (
    click
    ,
    change
    ) instead of descriptive names (
    handleNameChange
    )
Gotchas & Edge Cases:
  • Ref cleanup functions: TypeScript rejects non-null/undefined returns from ref callbacks -- use the cleanup pattern explicitly
  • useOptimistic
    state reverts automatically on failure -- no manual rollback needed
  • use()
    can be called conditionally (after early returns), unlike
    useContext
  • Error boundaries do not catch event handler errors, async errors, or SSR errors
  • useCallback
    without memoized children adds overhead without benefit
  • SSR requires
    typeof window !== "undefined"
    before accessing browser APIs
</red_flags>

<critical_reminders>
高优先级问题:
  • 在React 19中使用
    forwardRef
    ——已被弃用,应将
    ref
    作为常规属性传递
  • 在渲染
    <form>
    的组件中调用
    useFormStatus
    ——将始终返回
    pending: false
  • 当
    useActionState
    可用时,仍使用
    useState
    处理表单加载/错误状态——不必要的样板代码
  • 可复用组件未暴露
    className
    属性——阻止外部样式定制
  • 在try-catch块中调用
    use()
    ——应使用错误边界替代
中优先级问题:
  • 为无多维度变体的组件添加变体抽象
  • 无论子组件是否 memoization,都对所有处理器使用
    useCallback
    (过早优化)
  • 当React编译器自动处理memoization时,仍将Ref回调包裹在
    useCallback
    中
  • 使用通用事件处理器名称(
    click
    、
    change
    )而非描述性名称(
    handleNameChange
    )
陷阱与边缘情况:
  • Ref清理函数:TypeScript拒绝Ref回调返回非null/undefined的值——需显式使用清理模式
  • useOptimistic
    状态在失败时自动回滚——无需手动回滚
  • use()
    可在条件下调用(提前返回后),与
    useContext
    不同
  • 错误边界无法捕获事件处理器错误、异步错误或SSR错误
  • 无memoized子组件时使用
    useCallback
    会增加开销而无收益
  • SSR需在访问浏览器API前检查
    typeof window !== "undefined"
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

重要提醒

All code must follow project conventions in CLAUDE.md
(You MUST pass
ref
as a regular prop in React 19 -
forwardRef
is deprecated)
(You MUST expose
className
prop on ALL reusable components for customization)
(You MUST use
useActionState
for form submissions with pending/error state)
(You MUST call
useFormStatus
from a child component inside
<form>
, NOT in the component that renders the form)
Failure to follow these rules will break component composition, form state management, and styling flexibility.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(在React 19中必须将
ref
作为常规属性传递——
forwardRef
已被弃用)
(所有可复用组件必须暴露
className
属性以支持自定义样式)
(处理带有等待/错误状态的表单提交时必须使用
useActionState
)
(必须在
<form>
内部的子组件中调用
useFormStatus
,而非渲染表单的组件本身)
不遵循这些规则将破坏组件组合、表单状态管理和样式灵活性。
</critical_reminders>