React 19 新特性实战指南:use、Actions 表单与 ref-as-prop(基于 claude-skills react-expert 技能)
2026/9/17 9:11:14 网站建设 项目流程

React 19 新特性实战指南:use、Actions 表单与 ref-as-prop(基于 claude-skills react-expert 技能)

【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills

本指南以 claude-skills 仓库中 react-expert 技能的核心参考文档 react-19-features.md 为主体,系统讲解 React 19 的四大新 Hooks(use()useActionStateuseFormStatususeOptimistic)以及ref作为普通 prop 的突破性改变。读完本文,你将能够在 Next.js App Router 与纯客户端 React 应用中,用 Server Actions 搭建带加载态、错误态与乐观更新的表单,并用 Suspense 优雅处理数据获取,同时理解每个特性在当前仓库 Skill 工作流中的落地位置。

一、为什么 React 19 值得专门学习

React 19 是 React 生态的一次范式转移:数据获取、表单状态、DOM ref 这些过去需要大量样板代码的领域,如今被收编为内置 API。在 claude-skills 项目中,react-expert SKILL.md 明确将「React 19 features」列为技能的触发场景之一(triggers字段包含React 19Server ComponentsSuspense),并在「When to Use This Skill」中列出「Working with React 19 Server Components」「Implementing forms with React 19 actions」两大核心用例,其 Reference Guide 也把本主题指向references/react-19-features.md

这套文档的定位不是罗列特性,而是给出可直接复制的 TypeScript 代码模板,覆盖四类高频场景:Suspense 数据获取(use())、表单提交状态(useActionState+useFormStatus)、乐观更新(useOptimistic)、以及组件 API 简化(ref作为 prop)。下面逐一展开。

二、use()Hook:在渲染期间读取 Promise 与 Context

use()是 React 19 中一个位置灵活的新 API——它可以像其它 Hooks 一样在组件顶层调用,也可以(区别于useState/useEffect)在条件语句、循环或提前 return 之后调用。它的两种能力分别是:读取一个 Promise(配合 Suspense 挂起渲染直至其落定),以及读取 Context(作为useContext的替代)。

2.1 在渲染中读取 Promise

use()最典型的用法是读取 Promise 并让组件在 Promise 未 resolve 前挂起,由外层的<Suspense>负责展示 fallback:

import { use, Suspense } from 'react'; // Read promises in render function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) { const comments = use(commentsPromise); return ( <ul> {comments.map(c => <li key={c.id}>{c.text}</li>)} </ul> ); } // Parent creates promise, child reads it function Post({ postId }: { postId: string }) { const commentsPromise = fetchComments(postId); return ( <article> <PostContent id={postId} /> <Suspense fallback={<CommentsSkeleton />}> <Comments commentsPromise={commentsPromise} /> </Suspense> </article> ); }

关键要点(对应原文档 react-19-features.md 中的示例):

  • Promise 由父组件创建并传入fetchComments(postId)在父组件Post中发起,子组件Comments只负责use()它。这样数据请求在渲染真正挂起之前就已启动,避免了串行等待。
  • 挂起由外层 Suspense 兜底use(promise)使组件挂起时,最近的上层<Suspense>会渲染fallback(这里是CommentsSkeleton)。它与异步 Server Component 的流式渲染模式互补——相关内容参见 server-components.md 中「Streaming with Suspense」一节。
  • 不重复请求:由于 Promise 对象本身作为 prop 传递,同一 Promise 实例在重渲染间保持稳定,use()不会触发重复 fetch。

2.2 条件读取 Context

use(ThemeContext)useContext的替代写法,其独特价值在于可以放在条件分支内——这在之前用useContext会违反 Hooks 规则:

// Read context conditionally function Theme({ children }: { children: React.ReactNode }) { if (someCondition) { const theme = use(ThemeContext); return <div className={theme}>{children}</div>; } return children; }

对于「只有满足条件时才需要读取 Context」的场景(例如仅在生产环境注入主题、仅在启用实验功能时读取配置),这是唯一既简洁又合规的写法。全局状态的基础设施仍可沿用 state-management.md 中 Context +createContext的 Provider 模式,只是读取端在 React 19 中多了一个选择。

三、useActionState:把表单提交状态收进一个 Hook

在 React 19 之前,表单提交需要手动维护isPendingerrorsuccess等一堆useStateuseActionState用一个 Hook 打包了Action 函数 + 状态 + 待处理标志,常与 Server Actions 搭配('use server')使用。

原文档给出的 Newsletter 订阅示例(react-19-features.md)完整如下:

'use client'; import { useActionState } from 'react'; interface FormState { error?: string; success?: boolean; } async function submitAction(prevState: FormState, formData: FormData): Promise<FormState> { 'use server'; const email = formData.get('email') as string; try { await subscribe(email); return { success: true }; } catch { return { error: 'Failed to subscribe' }; } } function NewsletterForm() { const [state, formAction, isPending] = useActionState(submitAction, {}); return ( <form action={formAction}> <input name="email" type="email" required disabled={isPending} /> <button type="submit" disabled={isPending}> {isPending ? 'Subscribing...' : 'Subscribe'} </button> {state.error && <p className="error">{state.error}</p>} {state.success && <p className="success">Subscribed!</p>} </form> ); }

需要吃透的细节:

  • 签名useActionState(action, initialState)返回[state, formAction, isPending]action的签名固定为(prevState, formData) => nextStateinitialState是首次渲染时state的初始值。
  • 'use server'声明submitAction内部标注'use server'表示这是一个 Server Action,运行在服务端,可安全执行subscribe(email)这类涉及凭据或数据库的操作。注意此处文件同时有'use client'(组件)与函数内'use server'(Action)——这是 Server Action 从客户端组件中导入时的标准写法。
  • loading 状态isPending在 Action 执行期间为true,用于禁用输入框与按钮并切换文案;表单的 action 属性直接绑定formAction,无需手动onSubmit
  • 错误呈现:通过 Action 返回的state.error/state.success渲染内联提示,比传统在组件里 try/catch 更干净。

react-expert 技能自身的核心工作流模板(SKILL.md)给出了一个更精简的变体——submitForm(_prev, formData)返回问候消息字符串,直接useActionState(submitForm, '')并把返回的消息渲染成段落。这个模式可以推广到评论提交、登录、注册等任何「提交后需要回显结果」的表单。

四、useFormStatus:让子组件感知父级表单的 pending 状态

useFormStatus解决一个经典难题:提交按钮往往被抽成独立组件,无法直接拿到父级<form>的 pending 状态。它必须在<form>后代组件中调用,读取最近祖先表单的状态(原文档示例见 react-19-features.md):

'use client'; import { useFormStatus } from 'react-dom'; function SubmitButton() { const { pending, data, method, action } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? 'Submitting...' : 'Submit'} </button> ); } // Must be used inside a <form> function ContactForm() { return ( <form action={submitAction}> <input name="message" /> <SubmitButton /> </form> ); }

使用要点:

  • 注意导入来源useFormStatusreact-dom导出(而不是react),这是它与useActionStateuseOptimistic的区别。
  • 返回值{ pending, data, method, action }——pending表示表单是否正在提交;data是当前正在提交的FormDatamethod是 HTTP 方法;action是被调用的 Action 函数。常规场景只需pending
  • 职责分工useActionState负责「表单整体」的状态与 Action 绑定,useFormStatus负责「子组件」观察表单的提交中状态。二者常搭配出现:外层用useActionState管理error/success结果,内层按钮组件用useFormStatus统一处理禁用与 loading 文案,从而避免把按钮逻辑塞进每个表单。
  • 约束:若SubmitButton被渲染在<form>之外,pending始终为false,不会报错但也不会生效——因此文档中特意标注 "Must be used inside a<form>"。

五、useOptimistic:先渲染结果,再落盘

乐观更新(Optimistic UI)指的是:用户操作后立即用预期结果更新界面,后台请求完成后再用真实数据校准。useOptimistic把这一模式压缩成了两个返回值。

原文档的 Todo 列表示例(react-19-features.md):

'use client'; import { useOptimistic } from 'react'; function TodoList({ todos }: { todos: Todo[] }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo: Todo) => [...state, newTodo] ); async function addTodo(formData: FormData) { const text = formData.get('text') as string; // Immediately update UI addOptimisticTodo({ id: 'temp', text, completed: false }); // Then persist await createTodo(text); } return ( <> <ul> {optimisticTodos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> <form action={addTodo}> <input name="text" /> <button>Add</button> </form> </> ); }

理解这个 Hook 的三个关键点:

  • 签名useOptimistic(state, updateFn)返回[optimisticState, addOptimistic]。第一个参数是「真实状态」的基底(这里的todos),第二个参数是合并函数(currentState, optimisticValue) => newState,决定乐观值如何叠加到当前状态上。
  • 调用时机:在异步操作发起前调用addOptimisticTodo(...)立即更新界面(示例里用临时 id'temp'标记),随后await createTodo(text)持久化;当真实状态(todos)最终更新时,乐观值会被真实值替换。
  • 与表单 Action 的配合addTodo直接作为<form action>使用,配合FormData取值,说明乐观更新与 React 19 表单体系是同一套心智模型——先给用户反馈,再后台落地。

六、ref 作为 prop:不再需要forwardRef

React 19 之前,函数组件要接收ref必须包一层forwardRef。React 19 将ref降格为普通 prop,函数组件直接声明即可(原文档示例见 react-19-features.md):

// React 19: ref is just a prop function Input({ ref, ...props }: { ref?: React.Ref<HTMLInputElement> }) { return <input ref={ref} {...props} />; } // No need for forwardRef anymore function Form() { const inputRef = useRef<HTMLInputElement>(null); return <Input ref={inputRef} placeholder="Enter text" />; }

要点:

  • 类型ref在 props 中的类型为React.Ref<HTMLInputElement>,从 props 解构后直接传给原生<input>ref
  • 收益:消除了forwardRef这层包裹,组件树更扁平;对于需要透传 ref 的 UI 库组件,这是显著的心智简化。
  • 注意ref作为 prop 接收属于 React 19 的新行为;对于仍运行在 React 18 及以下的代码库,保留forwardRef写法仍然必要。这也解释了 react-expert 技能为何专门维护了 migration-class-to-modern.md 这类迁移指南——升级 React 19 时,forwardRef的大量调用点都是可以机械替换的。

七、快速参考:一张表选对 Hook,一张表用对模式

原文档在结尾用两张表做了浓缩总结(react-19-features.md),这里完整继承并补充使用场景说明:

Hook 速查

HookPurpose
use()Read promise/context in render
useActionState()Form action state + pending
useFormStatus()Form pending state (child)
useOptimistic()Optimistic UI updates
  • use():渲染期内联读取数据源;对应「Suspense 数据获取」与「条件 Context 读取」两种模式。
  • useActionState():表单 Action 的返回状态 + 提交中标志,替代手写三四个useState
  • useFormStatus():供<form>后代组件读取父表单的 pending 状态,专为抽离提交按钮设计。
  • useOptimistic():先渲染预期结果再持久化,适合点赞、评论、加购物车等即时反馈场景。

模式速查

PatternWhen
use(promise)Suspense data fetching
use(context)Conditional context read
useActionStateServer Actions with state

值得注意的边界:use(promise)适合「读 Promise」的数据流(尤其与 Suspense 组合),而大规模服务端状态缓存、失效与重取仍建议交给 TanStack Query 这类专职方案(参见 state-management.md);useActionState与 Server Actions 是「带状态的提交」首选,单纯无状态的表单提交直接用<form action={serverAction}>(见 server-components.md)即可。

八、在本技能工作流中的落地实践

react-expert 技能在 SKILL.md 中规定了标准工作流:分析需求 → 选择模式 → 用 TypeScript 实现 →tsc --noEmit校验 → 按需 memo 优化 → 用 React Testing Library 测试。使用上述 React 19 特性时应额外遵循以下约束(来自 SKILL.md 的 Constraints 节):

  • MUST DO:全程 TypeScript 严格模式;为异步组件配置错误边界(Error Boundary)兜底,避免use(promise)拒绝时白屏;Suspense 之外的异步 UI 也要有 loading 占位;对传给 memo 化子组件的回调使用useCallback
  • MUST NOT DO:直接修改 state(乐观更新要经由addOptimistic的 reducer 式合并函数,而非就地 push);用数组索引作列表key(示例中一律使用id或临时 id);在 JSX 内联创建函数导致多余重渲染。

一个典型组合是:useActionState管理提交结果与 pending,useFormStatus统一提交按钮的 loading 展示,useOptimistic在提交前即时回显,use(promise)负责评论等区块的 Suspense 加载——这四个 Hook 覆盖了现代 React 表单从「读取 → 提交 → 反馈」的完整链路。若要进一步系统化掌握配套模式,可继续阅读本技能下的 server-components.md、hooks-patterns.md 与 performance.md,其中分别覆盖 RSC 数据获取与 Server Actions、自定义 Hook 与 Effect 清理、以及memo/useTransition/lazy等性能手段——它们与本文的 React 19 特性共同构成 react-expert 技能的完整知识底座。

【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询