mcpField
W
web-framework-react

web-framework-react

github.com/agents-inc开发26100 次安装2 天前更新
代码仓库

关于 web-framework-react

面向 React 开发者的组件与 Hook 模式技能,重点涵盖 React 19。内容包括表单、ref、乐观更新、错误边界,以及适用于浏览器和服务端渲染的样式无关组件 API。

主要功能

  1. 01React 19 将 ref 作为属性及清理模式
  2. 02使用 useActionState 和 useFormStatus 管理表单状态
  3. 03乐观更新与 Promise 读取
  4. 04可复用组件、变体与自定义 Hook
  5. 05错误边界与运行时陷阱

适用场景

  1. 01将组件从 forwardRef 改为 ref 属性
  2. 02构建带等待和错误状态的表单
  3. 03添加乐观 UI 更新与可重试错误边界

安装

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

$npx skills add https://github.com/agents-inc/skills --skill web-framework-react

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

包含的文件

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

metadata.yaml
reference.md
examples/
core.md
hooks.md
react-19-hooks.md

详细介绍

React 模式

快速指南: React 19 确定了此技能所采用的四项做法——ref 是常规属性,forwardRef 已无必要;表单提交状态来自 useActionState;useFormStatus 只能从子组件读取外层表单的状态;ref 回调可以返回清理函数。组件保持与样式无关:公开 className,并通过 data-* 属性表示变体。

详细资源:


适用路径

  • 在浏览器中渲染的 React — 以下所有模式均可按原样使用;表单提交通过 examples/react-19-hooks.md 中的 useActionState 完成。
  • 由服务端框架渲染的 React — 提交和数据加载应使用该框架自己的服务端原语,因此跳过模式 6,组件、Hook、ref 和错误边界模式保持不变。

<critical_requirements>

编写 React 代码前

将 ref 作为常规属性传递。 React 19 无需 forwardRef 即可转发它,因此可以移除包装层和手动设置的 displayName。

每个可复用组件都应公开 className。 对于不了解此组件的使用者而言,这是唯一可用于设置组件样式的接口。

从在 <form> 内渲染的组件中调用 useFormStatus。 如果在渲染表单的组件中调用,它将永远返回 pending: false,且不会提示错误。

当表单需要待处理或错误状态时,优先使用 useActionState。 它会提供这两种状态,而且在水合之前,<form action={...}> 也能正常工作。

</critical_requirements>


自动检测: React 19、useActionState、useFormStatus、useOptimistic、use()、ref 作为属性、ref 清理、forwardRef 迁移、React.ComponentProps、getDerivedStateFromError、componentDidCatch、useCallback、自定义 Hook

适用范围:

  • 组件属性、组合和变体 API
  • 将 forwardRef 组件迁移为 ref 作为属性
  • 使用 React 19 Hook 处理表单提交、乐观更新和 Promise 读取
  • 用于复用有状态逻辑的自定义 Hook
  • 支持重试的错误边界

其他部分负责:

  • 样式——组件接收 className,并通过 data-* 公开变体;具体采用哪种 CSS 方案不在此技能的职责范围内
  • 生命周期长于组件的客户端状态,以及具有缓存和失效机制的服务端数据——组件通过属性接收两者,不必了解其来源
  • 路由,以及路由加载的数据
  • 网络请求的测试替身

核心模式

模式 1:组件结构

可复用组件会展开其元素自身的属性,直接接收 ref,并通过 data-* 属性表示变体,以便任何样式层都能选择这些属性。

export type ButtonProps = React.ComponentProps<"button"> & {
  variant?: "default" | "ghost" | "link";
  size?: "default" | "large" | "icon";
  ref?: React.Ref<HTMLButtonElement>;
};

export function Button({ variant = "default", size = "default", className, ref, ...props }: ButtonProps) {
  return <button className={className} data-variant={variant} data-size={size} ref={ref} {...props} />;
}

完整代码:examples/core.md


模式 2:带类型的变体属性

当组件有两个或更多视觉维度时,为其定义变体属性。只有一种视觉样式的组件只需接收 className,不需要其他属性——只有一个成员的联合类型只是没有第二种情况的抽象。

export type AlertVariant = "info" | "warning" | "error" | "success";

export function Alert({ variant = "info", className, ref, ...props }: AlertProps) {
  return <div ref={ref} className={className} data-variant={variant} {...props} />;
}

完整代码:examples/core.md


模式 3:事件处理器命名

handle 前缀用于内部处理器(handleNameChange),on 前缀用于回调属性(onSelect);每个处理器都要为其事件指定类型——例如 FormEvent<HTMLFormElement>、ChangeEvent<HTMLInputElement>——这样错误的字段访问就会在编译时失败。

完整代码:examples/core.md


模式 4:自定义 Hook

调用 Hook 但不渲染任何内容的逻辑,应使用以 use 为前缀的 Hook,而不是组件:分页状态、防抖值、持久化偏好设置。

完整代码:examples/hooks.md


模式 5:带重试功能的错误边界

错误边界会捕获渲染错误并返回一个重置函数,因此暂时性故障只会影响一个区域,而不是整个页面。应在每个功能区域周围放置一个错误边界,而不只是放在根节点。

interface Props {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

错误边界必须是类,因为 getDerivedStateFromError 没有对应的 Hook 形式。错误边界的放置、恢复和回退界面设计是独立的主题,应在本技能之外处理。


模式 6:使用 useActionState 提交表单

这个 Hook 会返回 action 的上次结果、传给 <form action={...}> 的 action,以及一个待处理标记——替代三次 useState 调用及其重置逻辑。

async function updateProfile(prevState: string | null, formData: FormData) {
  try {
    await saveProfile({ name: formData.get("name") as string });
    return null;
  } catch {
    return "Failed to save profile";
  }
}

const [error, submitAction, isPending] = useActionState(updateProfile, null);

完整代码:examples/react-19-hooks.md


模式 7:使用 useFormStatus 处理提交按钮

提交按钮会自行读取所属表单的待处理状态,因此一个按钮组件就能适用于所有表单,无须表单向下传递该标记。

import { useFormStatus } from "react-dom";

function SubmitButton({ children }: { children: React.ReactNode }) {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending} aria-busy={pending}>
      {pending ? "Submitting..." : children}
    </button>
  );
}

完整代码:examples/react-19-hooks.md


模式 8:使用 useOptimistic 提供即时反馈

立即渲染预期结果;如果请求失败,React 会将其恢复。setter 会在 startTransition 内调用。

const [optimisticItems, addOptimistic] = useOptimistic(
  items,
  (state, update: Item) => [...state, { ...update, pending: true }],
);

startTransition(async () => {
  addOptimistic(newItem);
  await saveItem(newItem);
});

完整代码:examples/react-19-hooks.md


模式 9:使用 use() 读取 Promise 和 context

use() 会在渲染期间读取 Promise 或 context;与 useContext 不同,它可以在提前返回之后调用。Promise 会暂停渲染,直到其解析,因此调用方必须位于 <Suspense> 下方;若 Promise 被拒绝,则会由错误边界捕获。

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise);
  return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>;
}

<Suspense fallback={<p>Loading...</p>}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>

完整代码:examples/react-19-hooks.md


模式 10:Ref 回调清理

Ref 回调可以返回一个清理函数,该函数会在卸载时运行——替代用于 DOM 设置的 useRef + useEffect 组合。

<video
  ref={(video) => {
    if (!video) return;
    video.play();
    return () => {
      video.pause();
      video.currentTime = 0;
    };
  }}
  src={src}
/>

完整代码:examples/react-19-hooks.md


<red_flags>

注意事项

运行时会出错:

  • 在渲染 <form> 的组件中调用 useFormStatus——pending 会一直为 false,按钮也永远不会被禁用;应在子组件中调用
  • 在 try/catch 中调用 use()——它会抛出异常以暂停渲染,因此 catch 会吞掉暂停;应改为用错误边界包裹调用方
  • 在 transition 或表单 action 之外调用 useOptimistic setter——React 会拒绝该调用,因此乐观值永远不会生效
  • 在 SSR 期间的渲染中读取浏览器 API——使用 typeof window !== "undefined" 进行保护,或将读取操作移至 effect 中
  • 依赖错误边界捕获事件处理程序、异步操作或 SSR 错误——它只捕获渲染错误,因此这些路径需要各自的 try/catch

行为可能令人意外:

  • 在 React 19 中,Ref 回调返回清理函数以外的任何内容都会导致类型错误,而且卸载时不再会以 null 调用该回调
  • useOptimistic 会自行在失败时恢复,因此手动回滚会与之冲突
  • 对传给未使用 memo 的子组件的处理程序使用 useCallback,会产生一次分配,却没有任何收益;无论如何,React Compiler 都会让这个包装变得多余
  • forwardRef 仍能编译,因此不会有任何提示指出这个包装器及其 displayName 已成冗余
  • 缺少 className 的组件完全无法由其使用方进行样式设置

</red_flags>

评论

支持 Markdown