Communitygithub.com

react-patterns

React 18/19 開發模式,包含 Hooks 規範、Server/Client 元件邊界、Suspense + Error Boundary、表單操作、資料擷取、狀態管理決策樹以及無障礙優先的組合模式。在編寫或審查 React 元件時使用。

react-patterns 是什麼?

這是一份針對 React 18 和 19 的完整開發模式指南,旨在幫助開發者建立穩健、高效且易於存取的元件樹。該技能涵蓋了從 Hooks 紀律到 Server/Client Component (RSC) 邊界的關鍵主題,並提供了狀態管理決策樹(從 local state 到 Zustand 或 TanStack Query),讓開發者在不同規模的應用中做出恰當選擇。詳細介紹了 React 19 的表單操作(useActionState)和樂觀更新(useOptimistic),並提供了 Suspense + Error Boundaries 的漸進式內容揭示策略。文件中還包含資料擷取決策表,指導何時使用 RSC fetch、TanStack Query、SWR 或原生 fetch,並強調避免在 useEffect 中直接 fetch 以避免競態條件。效能部分聚焦於 React.memo 的正確使用時機、避免重新渲染瀑布效應以及虛擬化長列表。無障礙優先的組合模式提供了 slots、compound components 和 render props 等多種技巧。此技能與 VSCode、Cursor、Claude Code 等開發工具相容,適合在撰寫或審查 JSX/TSX 程式碼時啟動,無論是使用 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 Patterns

慣用的 React 18/19 模式,用於建構穩健、易於存取、高效能的元件樹。

何時啟動

  • 編寫或修改 React 函式元件、自訂 Hooks 或元件樹
  • 審查 JSX/TSX 檔案
  • 設計狀態形狀或元件組合
  • 遷移 Class 元件或舊式大量使用 forwardRef/useEffect 的程式碼
  • 在 local state、提升狀態、Context 和外部儲存之間做選擇
  • 使用 Server Components / Client Components(Next.js App Router、RSC)
  • 使用 React 19 actions 或受控輸入實作表單
  • 使用 TanStack Query / SWR / RSC 連接資料擷取

核心原則

1. 渲染是 Prop 和 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. Side Effects 應在渲染之外

副作用、突變、網路請求和訂閱應放在事件處理器或 useEffect 中——絕不在渲染本體中。

3. 組合優於繼承

React 沒有元件的繼承模型。應使用 children、render props 或 component props 進行組合。

Hooks 規範

完整規則集請參閱 rules/react/hooks.md。重點如下:

  • 僅在最上層使用,不可條件式使用
  • 清理每個訂閱、計時器、監聽器
  • 當新的狀態依賴舊狀態時,使用 functional updater(setX(prev => prev + 1))
  • 預設立場:不要 memoize——僅在 profiler 或依賴鏈證明有必要時才加上 useMemo/useCallback
  • 僅當相同 hook 序列出現在 2 個以上元件時,才提取出自訂 hook

狀態位置決策樹

僅一個元件使用?
  -> 在該元件內部使用 useState

父元件加上少數後代使用?
  -> 提升到最近的共同祖先

跨遠距離分支使用,且讀取頻率低(主題、認證、語系)?
  -> React Context

高頻率更新且跨樹共享?
  -> 外部儲存(Zustand、Jotai、Redux Toolkit)

從伺服器推導?
  -> 伺服器狀態函式庫(TanStack Query、SWR、RSC fetch)

大多數頁面不需要 Context 或 global store。在重複提升變得痛苦之前,請抵抗抽象化的誘惑。

Server / Client Components (RSC)

// Server Component - 預設,非同步,永遠不為自己傳送 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} />;
}

// Client Component - 使用 "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>
  );
}

邊界:

  • Server -> Client:傳遞可序列化的 props 或 children
  • Client -> Server:透過 <form action={...}> 或從事件處理器命令式呼叫 Server Actions
  • 絕不要從 Client Component 檔案 import Server Component——應透過 children 組合它們

Suspense + Error Boundaries

<ErrorBoundary fallback={<ErrorView />}>
  <Suspense fallback={<UserSkeleton />}>
    <UserDetail id={id} />
  </Suspense>
</ErrorBoundary>
  • 將 Suspense 邊界放在資料附近,而非路由根節點——漸進式揭示內容
  • Error Boundary 仍然是 class API;使用 react-error-boundary 獲得 hook 友善的包裝
  • Boundary 會捕捉其子元件在渲染、生命週期和建構函式中拋出的錯誤——不包含事件處理器或非同步程式碼中的錯誤

表單

React 19 表單操作(新程式碼建議使用)

"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 / function-as-child

當父元件需要傳遞參數給渲染輸出時很有用:

<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 的改變就不會重新渲染 auth 消費者
  • 使用 useSyncExternalStore 處理外部狀態函式庫——對於安全的並行渲染是必需的

列表

  • 提供穩定的 key prop(資料庫 ID,而不是陣列索引)
  • 一旦可見項目數量超過約 50 且行數非簡單時,使用 @tanstack/react-virtual 或 react-window 虛擬化長列表

無障礙優先的組合

  • 在訴諸 role 屬性之前,總是先渲染語義 HTML(<button>、<a>、<nav>、<main>)
  • 每個互動元素必須可透過鍵盤到達
  • 表單輸入需要標籤——如果由圖示視覺標記,則使用 <label htmlFor> 或 aria-label
  • 在路由變更和 modal 開啟/關閉時管理焦點
  • 在元件測試中執行 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 用於框架比較
  • Agent:react-reviewer 用於程式碼審查,react-build-resolver 用於建構/bundler 錯誤
  • 指令:/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 的元件,在 notifications 變動時並不會重新渲染

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.

相關技能