react-patterns

react-patterns

热门

提供全面的 React 19 模式,涵盖服务端组件、服务端操作、useOptimistic、useActionState、useTransition、并发特性、Suspense 边界以及 TypeScript 集成。生成可执行的代码模式,验证公共端点的安全性,并通过 React Compiler 或手动记忆化优化性能。在构建使用 Next.js App Router 的 React 19 应用程序、实现乐观 UI 或优化并发渲染时主动使用。

311Star
37Fork
更新于 2026/6/22
SKILL.md
readonly只读
name
react-patterns
description

提供全面的 React 19 模式,涵盖服务端组件、服务端操作、useOptimistic、useActionState、useTransition、并发特性、Suspense 边界以及 TypeScript 集成。生成可执行的代码模式,验证公共端点的安全性,并通过 React Compiler 或手动记忆化优化性能。在构建使用 Next.js App Router 的 React 19 应用程序、实现乐观 UI 或优化并发渲染时主动使用。

React 19 开发模式

概述

适用于 Next.js App Router、服务端操作、乐观 UI 和并发特性的 React 19 模式。参见快速参考了解 API 摘要,以及示例获取可复制粘贴的模式。

使用时机

  • 使用 Next.js App Router 构建 React 19 应用程序
  • 使用 useOptimisticuseTransition 实现乐观 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 和上下文
乐观 UI useOptimistic (React 19) 突变操作的即时反馈
表单状态 useFormStatus (React 19) 子组件中的待处理状态
表单状态管理 useActionState (React 19) 服务端操作结果
自动记忆化 React Compiler 消除手动 memo/callback

操作指南

  1. 识别组件类型:确定需要服务端组件还是客户端组件
  2. 选择 Hooks:使用适当的 Hooks 进行状态管理和副作用处理
  3. 类型化 Props:为所有组件 props 定义 TypeScript 接口
  4. 处理异步:将数据获取组件包裹在 Suspense 边界中
  5. 优化:使用 React Compiler 或手动记忆化处理昂贵渲染
  6. 处理错误:添加 ErrorBoundary 实现优雅的错误处理
  7. 验证服务端操作:定义 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>
  );
}

最佳实践

服务端与客户端决策

  • 从服务端组件开始(无需指令)
  • 仅在需要 Hooks、浏览器 API 或事件处理程序时添加 'use client'

状态管理

  • 保持状态最小化 — 在渲染期间计算派生值,而非在 effect 中
  • 对于具有多个相关操作的状态,使用 useReducer
  • 将状态提升到最近的共同祖先

Effects

  • 仅将 effects 用于外部系统同步
  • 始终指定正确的依赖数组
  • 为订阅和定时器返回清理函数
  • 永远不要直接修改状态 — 始终创建新引用

性能

  • 使用 React Compiler 时:避免手动使用 useMemouseCallbackmemo
  • 未使用 React Compiler 时:对昂贵计算使用 useMemo,对稳定回调使用 useCallback
  • 对低优先级状态更新使用 useTransition
  • 使用稳定 ID 作为列表键,而非数组索引

React 19 特定

  • use(promise) 组件包裹在 Suspense 边界中
  • 使用 useActionState 实现表单与服务端操作的集成
  • 验证服务端操作输入 — 它们是公共端点
  • 从服务端组件向客户端组件传递可序列化数据

约束与警告

  • 服务端组件:不能使用 Hooks、事件处理程序或浏览器 API
  • use() Hook:只能在渲染期间调用,不能在回调或 effects 中调用
  • 服务端操作:必须包含 'use server' 指令;始终验证输入
  • 状态修改:永远不要直接修改状态 — 始终创建新引用
  • Effect 依赖:在 useEffect 依赖数组中包含所有依赖
  • 内存泄漏:始终在 useEffect 返回中清理订阅和事件监听器

参考资料

查阅以下文件获取详细模式: