mcpField
R
react-patterns

react-patterns

github.com/giuseppe-trisciuoglio开发3654.7k 次安装7 周前更新
代码仓库

关于 react-patterns

面向 React 开发者的 React 19 模式指南,涵盖 Next.js App Router 应用。内容按 Hooks、Server Actions、异步界面和性能优化等主题组织。

主要功能

  1. 01React 19 API 与 Hooks 模式
  2. 02Server 与 Client Component 指引
  3. 03乐观 UI 与 Server Actions
  4. 04Suspense 与异步渲染
  5. 05状态和性能模式

适用场景

  1. 01构建 Next.js App Router 应用
  2. 02实现带验证的 Server Action 表单
  3. 03为数据变更提供乐观反馈
  4. 04为复杂组件状态选择 Hooks

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill react-patterns

安装后在 Claude Code 中输入/react-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
component-patterns.md
hooks-patterns.md
learn.md
performance-patterns.md
react19-features.md
reference.md
typescript-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大型列表的搜索/筛选
延迟开销较大的 UIuseDeferredValue更新期间显示旧内容
读取资源use()(React 19)在渲染期间使用 Promise 和上下文
乐观 UIuseOptimistic(React 19)修改时即时反馈
表单状态useFormStatus(React 19)子组件中的待处理状态
表单状态useActionState(React 19)Server Action 结果
自动记忆化React Compiler无需手动使用 memo/callback

说明

  1. 确定组件类型:判断是否需要 Server Component 或 Client Component
  2. 选择 Hooks:使用适当的 Hooks 管理状态和副作用
  3. 定义 Props 类型:为所有组件 Props 定义 TypeScript 接口
  4. 处理异步操作:在 Suspense 边界中封装数据获取组件
  5. 优化:对开销较大的渲染使用 React Compiler 或手动记忆化
  6. 处理错误:添加 ErrorBoundary,以优雅地处理错误
  7. 验证 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 的返回函数中清理订阅和事件侦听器

参考资料

有关详细模式,请参阅以下文件:

评论

支持 Markdown