SKILL.md
唯讀
名稱
react-patterns
描述
提供全面的 React 19 模式,涵蓋伺服器元件、伺服器動作、useOptimistic、useActionState、useTransition、並行功能、Suspense 邊界以及 TypeScript 整合。可生成可執行的程式碼模式,驗證公開端點的安全性,並透過 React Compiler 或手動記憶化最佳化效能。在建置 React 19 應用程式搭配 Next.js App Router、實作樂觀 UI 或最佳化並行渲染時,主動使用此技能。
React 19 開發模式
概述
適用於 Next.js App Router、伺服器動作、樂觀 UI 和並行功能的 React 19 模式。請參閱快速參考以了解 API 摘要,以及範例以取得可複製貼上的模式。
使用時機
- 使用 Next.js App Router 建置 React 19 應用程式
- 使用
useOptimistic或useTransition實作樂觀 UI - 建立含表單驗證的伺服器動作
- 從類別元件遷移至 hooks
- 使用 React Compiler 最佳化並行渲染
- 使用
useReducer或自訂 hooks 管理複雜狀態 - 將非同步操作包裝在 Suspense 邊界中
快速參考
| 模式 | Hook / API | 使用情境 |
|---|---|---|
| 本地狀態 | useState |
簡單的元件狀態 |
| 複雜狀態 | useReducer |
多動作狀態機 |
| 副作用 | useEffect |
訂閱、資料擷取 |
| 共享狀態 | useContext / createContext |
跨元件資料 |
| DOM 存取 | useRef |
焦點、測量、計時器 |
| 效能 | useMemo / useCallback |
昂貴的計算 |
| 非緊急更新 | useTransition |
大型列表的搜尋/篩選 |
| 延遲昂貴 UI | useDeferredValue |
保留舊值直到更新完成 |
| 讀取資源 | use() (React 19) |
在渲染中使用 Promise 和 context |
| 樂觀 UI | useOptimistic (React 19) |
變更操作的即時回饋 |
| 表單狀態 | useFormStatus (React 19) |
子元件中的待處理狀態 |
| 表單狀態 | useActionState (React 19) |
伺服器動作結果 |
| 自動記憶化 | React Compiler | 消除手動 memo/callback |
使用說明
- 識別元件類型:判斷需要伺服器元件還是客戶端元件
- 選擇 Hooks:使用適當的 hooks 進行狀態管理和副作用
- 型別 Props:為所有元件 props 定義 TypeScript 介面
- 處理非同步:將資料擷取元件包裝在 Suspense 邊界中
- 最佳化:使用 React Compiler 或手動記憶化處理昂貴的渲染
- 處理錯誤:加入 ErrorBoundary 以優雅處理錯誤
- 驗證伺服器動作:定義 Zod/schema 驗證,然後測試:
- 提交無效輸入 → 驗證拒絕
- 提交有效輸入 → 驗證成功
範例
伺服器元件與客戶端互動
// 伺服器元件(預設)— 非同步,擷取資料
async function ProductPage({ id }: { id: string }) {
const product = await db.product.findUnique({ where: { id } });
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} />
</div>
);
}
// 客戶端元件 — 處理互動
'use client';
function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition();
const handleAdd = () => {
startTransition(async () => {
await addToCart(productId);
});
};
return (
<button onClick={handleAdd} disabled={isPending}>
{isPending ? '加入中...' : '加入購物車'}
</button>
);
}
useOptimistic 實現即時回饋
'use client';
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (t: Todo) => Promise<void> }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, { ...newTodo, pending: true }]
);
const handleSubmit = async (formData: FormData) => {
const newTodo = { id: Date.now(), text: formData.get('text') as string };
addOptimisticTodo(newTodo); // 立即更新 UI
await addTodo(newTodo); // 實際後端呼叫
};
return (
<form action={handleSubmit}>
{optimisticTodos.map(todo => (
<div key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</div>
))}
<input type="text" name="text" />
<button type="submit">新增</button>
</form>
);
}
含表單的伺服器動作
// app/actions.ts
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
const schema = z.object({
title: z.string().min(5),
content: z.string().min(10),
});
export async function createPost(prevState: any, formData: FormData) {
const parsed = schema.safeParse({
title: formData.get('title'),
content: formData.get('content'),
});
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors };
}
await db.post.create({ data: parsed.data });
revalidatePath('/posts');
return { success: true };
}
// app/blog/new/page.tsx
'use client';
import { useActionState } from 'react';
import { createPost } from '../actions';
export default function NewPostPage() {
const [state, formAction, pending] = useActionState(createPost, {});
return (
<form action={formAction}>
<input name="title" placeholder="標題" />
{state.errors?.title && <span>{state.errors.title[0]}</span>}
<textarea name="content" placeholder="內容" />
<button type="submit" disabled={pending}>
{pending ? '發布中...' : '發布'}
</button>
</form>
);
}
自訂 Hook
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
useTransition 用於非緊急更新
function SearchableList({ items }: { items: Item[] }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [filteredItems, setFilteredItems] = useState(items);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
startTransition(() => {
setFilteredItems(items.filter(i => i.name.toLowerCase().includes(e.target.value.toLowerCase())));
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <span>篩選中...</span>}
<ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>
</div>
);
}
最佳實務
伺服器 vs 客戶端決策
- 從伺服器元件開始(不需要指令)
- 僅在需要 hooks、瀏覽器 API、事件處理器時加入
'use client'
狀態管理
- 保持狀態最小化 — 在渲染期間計算衍生值,而非在 effects 中
- 對具有多個相關動作的狀態使用
useReducer - 將狀態提升到最近的共同祖先
Effects
- 僅將 effects 用於外部系統同步
- 始終指定正確的依賴陣列
- 為訂閱和計時器回傳清理函式
- 永遠不要直接變異狀態 — 始終建立新的參考
效能
- 使用 React Compiler:避免手動
useMemo、useCallback、memo - 未使用 React Compiler:對昂貴計算使用
useMemo,對穩定回呼使用useCallback - 對低優先順序的狀態更新使用
useTransition - 使用穩定的 ID 作為列表鍵值,而非陣列索引
React 19 特定事項
- 將
use(promise)元件包裝在 Suspense 邊界中 - 使用
useActionState進行表單-伺服器動作整合 - 驗證伺服器動作輸入 — 它們是公開端點
- 從伺服器元件傳遞可序列化的資料到客戶端元件
限制與警告
- 伺服器元件:不能使用 hooks、事件處理器或瀏覽器 API
- use() Hook:只能在渲染期間呼叫,不能在回呼或 effects 中
- 伺服器動作:必須包含
'use server'指令;始終驗證輸入 - 狀態變異:永遠不要直接變異狀態 — 始終建立新的參考
- Effect 依賴:在
useEffect依賴陣列中包含所有依賴 - 記憶體洩漏:始終在 useEffect 回傳中清理訂閱和事件監聽器
參考資料
請查閱以下檔案以取得詳細模式:
- references/hooks-patterns.md — useState、useEffect、useRef、useReducer、自訂 hooks、常見陷阱
- references/component-patterns.md — Props、組合、提升狀態、context、複合元件、錯誤邊界
- references/react19-features.md — use()、useOptimistic、useFormStatus、useActionState、伺服器動作、伺服器元件、遷移指南
- references/performance-patterns.md — React Compiler 設定、useMemo、useCallback、useTransition、useDeferredValue、懶載入
- references/typescript-patterns.md — 型別 props、泛型元件、事件處理器、區分聯合、context 型別
- references/learn.md — 從基礎到進階 React 19 的漸進學習指南
- references/reference.md — 所有 React hooks 和元件 API 的完整 API 參考






