Communitygithub.com

react-patterns

React 18/19 模式,包括 Hooks 规范、服务端/客户端组件边界、Suspense + 错误边界、表单操作、数据获取、状态管理决策树以及无障碍优先的组合。在编写或审查 React 组件时使用。

react-patterns 是什么?

该技能提供了构建健壮、可访问且高性能 React 组件树的惯用 React 18/19 模式。它涵盖了核心原则,例如将渲染视为 props 和 state 的纯函数、在渲染之外处理副作用以及优先使用组合而非继承。技能详细介绍了 Hooks 规范、状态位置决策树,以及服务端组件(RSC)和客户端组件之间的边界。它还包括使用 Suspense 和错误边界实现渐进式内容加载、使用 React 19 的 `useActionState` 和 `useOptimistic` 处理表单的最佳实践,以及一个用于客户端与服务器端数据获取的数据获取决策矩阵。此外,该技能提供了性能优化指导(如 `React.memo` 的使用时机和避免渲染级联)、无障碍优先的组合原则,以及通过使用自定义 Hooks 和组件组合(包括插槽和复合组件模式)的具体代码示例。该技能设计为与路由无关,旨在作为跨不同 React 框架(如 Next.js、React Router、TanStack Router)可重用的核心模式集。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/react-patterns

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

React 模式

用于构建健壮、可访问、高性能组件树的惯用 React 18/19 模式。

何时激活

  • 编写或修改 React 函数组件、自定义 Hooks 或组件树
  • 审查 JSX/TSX 文件
  • 设计状态结构或组件组合
  • 迁移类组件或大量使用旧的 forwardRef/useEffect 的代码
  • 在局部状态、提升的状态、Context 和外部存储之间做出选择
  • 使用服务端组件/客户端组件(Next.js App Router、RSC)
  • 使用 React 19 actions 或受控输入实现表单
  • 使用 TanStack Query / SWR / RSC 进行数据获取

核心原则

1. 渲染是 Props 和 State 的纯函数

// 好:在渲染期间进行派生
function Cart({ items }: { items: CartItem[] }) {
  const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
  return <span>{formatMoney(total)}</span>;
}

// 坏:将派生状态单独存储
function Cart({ items }: { items: CartItem[] }) {
  const [total, setTotal] = useState(0);
  useEffect(() => {
    setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0));
  }, [items]);
  return <span>{formatMoney(total)}</span>;
}

在 useEffect 中处理派生状态会增加一个渲染周期、可能导致同步问题,并且会模糊数据流。

2. 副作用在渲染之外

Effects(副作用)、突变、网络调用和订阅应位于事件处理器或 useEffect 中——永远不要放在渲染函数体内。

3. 组合优于继承

React 没有组件的继承模型。使用 children、render props 或组件 props 进行组合。

Hooks 规范

完整的规则集请参见 rules/react/hooks.md。要点如下:

  • 仅在顶层调用,永远不要在条件语句中调用
  • 清理每个订阅、定时器、事件监听器
  • 当新状态依赖于旧状态时,使用函数式更新(setX(prev => prev + 1))
  • 默认立场:不要进行记忆化——仅在性能分析器或依赖链证明有必要时才添加 useMemo/useCallback
  • 仅当相同的 Hook 序列出现在 2 个以上的组件中时,才将其提取为自定义 Hook

状态位置决策树

仅由一个组件使用?
  -> 在该组件内使用 useState

由父组件和少数后代使用?
  -> 提升到最近的共同祖先

由不同分支使用且读取频率低(主题、认证、区域设置)?
  -> React Context

在组件树中高频更新共享?
  -> 外部存储(Zustand、Jotai、Redux Toolkit)

从服务端派生而来?
  -> 服务端状态库(TanStack Query、SWR、RSC fetch)

大多数页面不需要 Context 或全局存储。在重复提升变得痛苦之前,抵制抽象化的冲动。

服务端 / 客户端组件(RSC)

// 服务端组件 - 默认,异步,永远不自行发送 JS
export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.product.findUnique({ where: { id: params.id } });
  if (!product) notFound();
  return <ProductView product={product} />;
}

// 客户端组件 - 通过 "use client" 选择加入
"use client";
export function AddToCartButton({ productId }: { productId: string }) {
  const [pending, startTransition] = useTransition();
  return (
    <button
      disabled={pending}
      onClick={() => startTransition(() => addToCart(productId))}
    >
      {pending ? "Adding..." : "Add to cart"}
    </button>
  );
}

边界:

  • 服务端 -> 客户端:传递可序列化的 props 或 children
  • 客户端 -> 服务端:通过 <form action={...}> 调用服务端 Actions,或从事件处理器中命令式调用
  • 永远不要从客户端组件文件中 import 服务端组件——改为通过 children 进行组合

Suspense + 错误边界

<ErrorBoundary fallback={<ErrorView />}>
  <Suspense fallback={<UserSkeleton />}>
    <UserDetail id={id} />
  </Suspense>
</ErrorBoundary>
  • 将 Suspense 边界放置在数据附近,而不是路由根节点——逐步揭示内容
  • Error Boundary 仍然是类组件 API;使用 react-error-boundary 获得更友好的 Hook 包装
  • 边界捕获其子组件在渲染、生命周期和构造函数期间抛出的错误——不捕获事件处理器或异步代码中的错误

表单

React 19 表单 actions(新代码首选)

"use client";
import { useActionState } from "react";

const initial = { error: null as string | null };

async function updateUserAction(_prev: typeof initial, formData: FormData) {
  "use server";
  const parsed = UserSchema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { error: "Invalid input" };
  await db.user.update({ where: { id: parsed.data.id }, data: parsed.data });
  return { error: null };
}

export function UserForm() {
  const [state, formAction, pending] = useActionState(updateUserAction, initial);
  return (
    <form action={formAction}>
      <input name="name" required />
      <button type="submit" disabled={pending}>Save</button>
      {state.error && <p role="alert">{state.error}</p>}
    </form>
  );
}

受控输入

当值驱动其他 UI、每次按键都格式化或实现实时验证时,使用受控组件。

复杂表单

对于多步骤表单、动态字段数组或跨字段验证:使用库(React Hook Form、TanStack Form)。对于超过简单复杂度的表单,自行管理状态是一个维护陷阱。

数据获取决策矩阵

需求工具
每个请求的数据(Next.js App Router)RSC await fetch()
客户端缓存 + 变更 + 失效TanStack Query
轻量级客户端缓存 + 重新验证SWR
实时订阅Server-Sent Events、WebSockets 或库的订阅 API
一次性即发即忘事件处理器中的 fetch()

避免使用 useEffect + fetch 进行应用数据获取——会引发竞态条件、无缓存、无重试、无法集成 Suspense。

组合模式

通过 children 实现的插槽

<Layout>
  <Header />
  <Main>{content}</Main>
</Layout>

命名插槽

<Page header={<Nav />} sidebar={<Filters />}>
  <Results />
</Page>

复合组件(通过 Context 共享状态)

<Tabs defaultValue="profile">
  <Tabs.List>
    <Tabs.Trigger value="profile">Profile</Tabs.Trigger>
    <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Panel value="profile"><Profile /></Tabs.Panel>
  <Tabs.Panel value="settings"><Settings /></Tabs.Panel>
</Tabs>

Render prop / 函数作为子元素

当父组件需要向渲染输出传递参数时很有用:

<DataLoader id={id}>
  {({ data, isLoading }) => isLoading ? <Spinner /> : <UserCard user={data} />}
</DataLoader>

现代替代方案:返回相同形状的 Hook(useData(id))——通常更清晰。

性能

何时 React.memo 才真正有帮助

仅在以下情况将组件包裹在 React.memo 中:

  1. 该组件频繁重新渲染
  2. 其 props 在渲染之间通常是相同的
  3. 其渲染开销可衡量地昂贵

React.memo 在每次渲染时都会增加一个相等性检查。如果大多数渲染的 props 都不同,那么这个检查就是纯粹的额外开销。

避免渲染级联

  • 尽可能将状态下移而不是上提
  • 拆分 Context:每个关注点一个 Context,这样 themeContext 的改变不会导致认证消费者重新渲染
  • 对于外部状态库,使用 useSyncExternalStore——对于安全的并发渲染是必需的

列表

  • 提供稳定的 key prop(数据库 ID,而不是数组索引)
  • 当可见项数超过 ~50 且行非平凡时,使用 @tanstack/react-virtual 或 react-window 虚拟化长列表

无障碍优先的组合

  • 在求助于 role 属性之前,始终渲染语义化 HTML(<button>、<a>、<nav>、<main>)
  • 每个可交互元素必须可通过键盘访问
  • 表单输入需要标签——<label htmlFor> 或如果由图标视觉标签,则使用 aria-label
  • 在路由变化和模态框打开/关闭时管理焦点
  • 在组件测试中运行 axe(参见 skills/react-testing)
  • 交叉参考:skills/accessibility/SKILL.md 涵盖了 WCAG 标准和模式库

路由

此技能与路由无关。上述模式适用于 React Router、TanStack Router、Next.js App Router、Remix Router。路由特定的模式(loaders、actions、嵌套布局)请遵循路由框架的文档——这些是建立在 React 核心之上的框架关注点。

不适用内容(指针章节)

  • Next.js 细节:App Router 数据加载、Route Handlers、Middleware、Parallel Routes——分开关注,请使用 Next.js 文档
  • React Native:平台特定的模式差异足够大,需要单独的 react-native-patterns 技能(目前不存在)
  • Remix:Loader/action 约定与 RSC 有重叠,但请遵循 Remix 文档

相关资源

  • 规则:rules/react/——编码风格、Hooks、模式、安全性、测试
  • 技能:react-performance 用于 Vercel 派生的性能规则集,frontend-patterns 用于跨框架 UI 关注点,accessibility,angular-developer 用于框架比较
  • Agents:react-reviewer 用于代码审查,react-build-resolver 用于构建/打包器错误
  • 命令:/react-review、/react-build、/react-test

示例

用于防抖搜索的自定义 Hook

function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

function SearchBox() {
  const [query, setQuery] = useState("");
  const debounced = useDebounce(query, 300);
  const { data } = useQuery({
    queryKey: ["search", debounced],
    queryFn: () => searchApi(debounced),
    enabled: debounced.length > 0,
  });
  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <Results items={data ?? []} />
    </>
  );
}

使用 React 19 useOptimistic 实现乐观 UI

"use client";
import { useOptimistic } from "react";

export function MessageList({ messages }: { messages: Message[] }) {
  const [optimistic, addOptimistic] = useOptimistic(
    messages,
    (state, newMessage: Message) => [...state, newMessage],
  );

  async function send(formData: FormData) {
    const text = String(formData.get("text"));
    addOptimistic({ id: "pending", text, sender: "me" });
    await saveMessage(text);
  }

  return (
    <>
      <ul>{optimistic.map((m) => <li key={m.id}>{m.text}</li>)}</ul>
      <form action={send}>
        <input name="text" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

拆分 Context 以避免渲染级联

// 两个 Context:一个很少变化,一个频繁变化
const ThemeContext = createContext<Theme>("light");
const NotificationsContext = createContext<Notification[]>([]);

// 一个只消费 ThemeContext 的组件不会在通知改变时重新渲染

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能