关于 react-patterns
面向 React 开发者的 React 19 模式指南,涵盖 Next.js App Router 应用。内容按 Hooks、Server Actions、异步界面和性能优化等主题组织。
主要功能
- 01React 19 API 与 Hooks 模式
- 02Server 与 Client Component 指引
- 03乐观 UI 与 Server Actions
- 04Suspense 与异步渲染
- 05状态和性能模式
适用场景
- 01构建 Next.js App Router 应用
- 02实现带验证的 Server Action 表单
- 03为数据变更提供乐观反馈
- 04为复杂组件状态选择 Hooks
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill react-patterns安装后在 Claude Code 中输入/react-patterns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/component-patterns.mdhooks-patterns.mdlearn.mdperformance-patterns.mdreact19-features.mdreference.mdtypescript-patterns.md详细介绍
React 19 开发模式
概述
适用于 Next.js App Router、Server Actions、乐观 UI 和并发功能的 React 19 模式。API 摘要请参阅快速参考,复制粘贴模式请参阅示例。
适用场景
- 使用 Next.js App Router 构建 React 19 应用
- 使用
useOptimistic或useTransition实现乐观 UI - 创建带表单验证的 Server Actions
- 从类组件迁移到 Hooks
- 使用 React Compiler 优化并发渲染
- 使用
useReducer或自定义 Hooks 管理复杂状态 - 在 Suspense 边界中封装异步操作
快速参考
| 模式 | Hook / API | 用例 |
|---|---|---|
| 本地状态 | useState | 简单的组件状态 |
| 复杂状态 | useReducer | 多操作状态机 |
| 副作用 | useEffect | 订阅、数据获取 |
| 共享状态 | useContext / createContext | 跨组件数据 |
| DOM 访问 | useRef | 焦点、测量、计时器 |
| 性能 | useMemo / useCallback | 开销较大的计算 |
| 非紧急更新 | useTransition | 大型列表的搜索/筛选 |
| 延迟开销较大的 UI | useDeferredValue | 更新期间显示旧内容 |
| 读取资源 | use()(React 19) | 在渲染期间使用 Promise 和上下文 |
| 乐观 UI | useOptimistic(React 19) | 修改时即时反馈 |
| 表单状态 | useFormStatus(React 19) | 子组件中的待处理状态 |
| 表单状态 | useActionState(React 19) | Server Action 结果 |
| 自动记忆化 | React Compiler | 无需手动使用 memo/callback |
说明
- 确定组件类型:判断是否需要 Server Component 或 Client Component
- 选择 Hooks:使用适当的 Hooks 管理状态和副作用
- 定义 Props 类型:为所有组件 Props 定义 TypeScript 接口
- 处理异步操作:在 Suspense 边界中封装数据获取组件
- 优化:对开销较大的渲染使用 React Compiler 或手动记忆化
- 处理错误:添加 ErrorBoundary,以优雅地处理错误
- 验证 Server Actions:定义 Zod/schema 验证,然后进行测试:
- 提交无效输入 → 确认请求被拒绝
- 提交有效输入 → 确认操作成功
示例
带客户端交互的 Server Component
// Server Component (default) — async, fetches data
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>
);
}
// Client Component — handles interactivity
'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 ? 'Adding...' : 'Add to Cart'}
</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); // Immediate UI update
await addTodo(newTodo); // Actual backend call
};
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">Add</button>
</form>
);
}
带表单的 Server Action
// 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="Title" />
{state.errors?.title && <span>{state.errors.title[0]}</span>}
<textarea name="content" placeholder="Content" />
<button type="submit" disabled={pending}>
{pending ? 'Publishing...' : 'Publish'}
</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>Filtering...</span>}
<ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>
</div>
);
}
最佳实践
Server 与 Client 的选择
- 从 Server Component 开始(无需指令)
- 仅在需要 Hooks、浏览器 API 或事件处理程序时添加
'use client'
状态管理
- 尽量减少状态——在渲染期间计算派生值,而不是在 Effects 中计算
- 对包含多个相关操作的状态使用
useReducer - 将状态提升到最近的公共祖先组件
Effects
- 仅使用 Effects 与外部系统同步
- 始终指定正确的依赖数组
- 为订阅和计时器返回清理函数
- 切勿直接修改状态——始终创建新的引用
性能
- 使用 React Compiler 时:避免手动使用
useMemo、useCallback和memo - 未使用 React Compiler 时:对开销较大的计算使用
useMemo,对稳定的回调使用useCallback - 对低优先级状态更新使用
useTransition - 使用稳定 ID 作为列表键,不要使用数组索引
React 19 特有说明
- 在 Suspense 边界中封装使用
use(promise)的组件 - 使用
useActionState集成表单与 Server Action - 验证 Server Action 输入——它们是公开端点
- 从 Server Component 向 Client Component 传递可序列化数据
约束和警告
- 服务端组件:不能使用 hooks、事件处理程序或浏览器 API
- use() Hook:只能在渲染期间调用,不能在回调或副作用中调用
- 服务端操作:必须包含
'use server'指令;始终验证输入 - 状态变更:绝不直接修改状态——始终创建新引用
- 副作用依赖项:在
useEffect依赖数组中包含所有依赖项 - 内存泄漏:始终在
useEffect的返回函数中清理订阅和事件侦听器
参考资料
有关详细模式,请参阅以下文件:
- references/hooks-patterns.md — useState、useEffect、useRef、useReducer、自定义 hooks、常见陷阱
- references/component-patterns.md — Props、组合、状态提升、上下文、复合组件、错误边界
- references/react19-features.md — use()、useOptimistic、useFormStatus、useActionState、服务端操作、服务端组件、迁移指南
- references/performance-patterns.md — React Compiler 设置、useMemo、useCallback、useTransition、useDeferredValue、延迟加载
- references/typescript-patterns.md — 类型化 props、泛型组件、事件处理程序、可辨识联合、上下文类型
- references/learn.md — 从 React 19 基础到进阶的渐进式学习指南
- references/reference.md — 所有 React hooks 和组件 API 的完整参考
评论
支持 Markdown