React 学习站
React›前沿与实践›前沿

React 19 新特性与 Actions

前沿前沿与实践

一句话定义

React 19(2024 年底稳定)把「异步提交」变成一等公民:Actions(<form action> + useActionState + useOptimistic + use())封装了 pending/错误/乐观更新的全套样板,同时 ref 可作普通 prop、文档元数据可直接写进组件、React Compiler 自动记忆化。

为什么重要

表单提交曾是 React 样板代码最密集的场景(state×3 + handler + 竞态 + 错误提示);Actions 把它压缩成「一个函数 + 两个 Hook」。同时本篇标记学习路线的「前沿」边界:这些 API 正在成为新项目的默认写法。

前置知识

  • kp-011 受控表单(Actions 是它的对照系)。
  • kp-022 过渡优先级(Actions 建立其上)。
  • kp-024 RSC(Server Actions 是 Actions 的服务端形态)。

核心概念

  • Action:传给 <form action={fn}> 的异步函数;React 自动管理 pending、错误边界语义与重置非受控表单。
  • useActionState(fn, initState):包装 action,拿到 [state, formAction, isPending]——state 是上一次提交的返回值。
  • useOptimistic(state, reducer):在异步完成前先显示「乐观结果」,完成后自动回滚/校正为真实状态。
  • use(promise / context):在渲染期间读取 Promise/Context 的同步写法(配 Suspense)。
  • ref 作为 prop:函数组件直接接收 ref,forwardRef 退居兼容层。
  • 文档元数据:<title>、<meta> 可直接写在组件 JSX 里,React 自动提升到 <head>。
  • React Compiler:自动记忆化(useMemo/useCallback/memo 的大多数手写场景),19 时代逐步落地(以官方发布状态为准)。

原理 / 机制

旧写法 → Actions 的演进:

jsx// 旧:三 state + handler + 错误处理(kp-011 的完整负担)
const [name, setName] = useState('');
const [pending, setPending] = useState(false);
const [error, setError] = useState(null);
async function onSubmit(e) {
  e.preventDefault();
  setPending(true);
  try { await submit(name); } catch (err) { setError(err); }
  finally { setPending(false); }
}

// 新:Actions 一条龙
'use client';
import { useActionState } from 'react';

async function createAction(prev, formData) {
  const name = formData.get('name');           // 非受控取值!
  const err = await api.create(name);          // 抛错/返回都行
  return err ?? null;                          // 返回值成为新 state
}

function CreateForm() {
  const [error, formAction, isPending] = useActionState(createAction, null);
  return (
    <form action={formAction}>
      <input name="name" />
      <button disabled={isPending}>{isPending ? '提交中…' : '创建'}</button>
      {error && <p role="alert">{error.message}</p>}
    </form>
  );
}

乐观更新(点 like 立刻 +1,服务器失败自动回滚):

jsxfunction LikeButton({ postId, initial }) {
  const [likes, setLikes] = useOptimistic(initial);
  return (
    <button onClick={async () => {
      setLikes(l => l + 1);                  // 乐观显示(自动在请求结束后校正)
      await api.like(postId);                // 失败则回滚为真实值
    }}>
      👍 {likes}
    </button>
  );
}

Server Action(RSC 体系的上行通道,kp-024 的配对概念):

jsx// app/actions.js(服务端文件)
'use server';
export async function createPost(formData) {
  'use server';
  await db.post.create({ title: formData.get('title') });
  revalidatePath('/posts');                  // 通知客户端 RSC 缓存失效
}

// 任意 Client Component
<form action={createPost}>…</form>           // 函数引用跨边界安全(由框架序列化)

直观类比

旧表单像手写报销单:你自己填三联单(三个 state)、自己跑流程(handler)、自己等章(pending)、盖错了自己撕(错误重置)。Actions 像自动报销系统:把单子丢进扫码口(form action),系统自动显示「处理中」(isPending)、失败自动退单(state 返回错误)、成功自动归档(表单重置);「乐观更新」则是先给你盖个「预计通过」的电子章,系统拒绝时章自动消失。

实例 / 案例

「表单 + 乐观列表」的组合(学习路径终局项目的核心片段):

jsx'use client';
function CommentList({ initialComments, postId }) {
  const [comments, addComment] = useOptimistic(
    initialComments,
    (list, newOne) => [...list, newOne]
  );

  async function formAction(formData) {
    const text = formData.get('text');
    addComment({ id: Date.now(), text, pending: true }); // 乐观插入
    await api.comment(postId, text);                      // 真实提交
  }

  return (
    <>
      <form action={formAction}>
        <input name="text" required />
        <button>发送</button>
      </form>
      <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
    </>
  );
}

零 loading state、零 error state、零手动重置——三条样板线全部消失,这就是 Actions 的价值量化。

常见误区

  • 以为 Actions 只服务 RSC:Client 场景(纯 SPA 里的 <form action>)同样可用;Server Action 只是它的一种。
  • useActionState 的 fn 签名记错:第一个参数是上一次的 state,第二个才是 formData——新手第一坑。
  • 在 action 里 setState 混用旧模式:Actions 接管了 pending/错误语义,再手动 setState 会互相打架。
  • 乐观更新不做服务端校验:useOptimistic 的回滚依赖真实结果;不要在失败时假装成功。
  • 以为 Compiler 让 useMemo 全部可删:自动记忆化覆盖「组件内」场景,跨组件引用稳定性需求(如依赖自定义 Hook 的行为契约)仍需人工判断;迁移期以官方指引为准。

自测题

  1. 写出 useActionState 的三返回值与 fn 签名;「prev」参数的用途是什么?
  2. useOptimistic 的回滚机制依赖什么?失败场景下 UI 会经历哪两帧?
  3. Server Action 与 Client <form action> 的区别?revalidatePath 做了什么?
  4. 「有了 Actions 就不需要受控表单了」——对吗?说出仍需受控的两种场景(提示:实时校验、字符过滤,kp-011)。
  5. ref 作为 prop 与 forwardRef 的关系?

与其他知识点的关系

  • 向前:受控表单(kp-011)的范式对照;并发优先级(kp-022)的封装。
  • 向后:kp-026 中 Next.js 的 Server Actions 是其完整工程形态;kp-028 审查清单新增「表单是否可用 Actions 简化」检查项;学习路径终局项目直接使用本篇 API。

延伸阅读

  • React 19 发布公告:<https://react.dev/blog/2024/12/05/react-19>
  • useActionState / useOptimistic / use API(官方参考):<https://react.dev/reference/react/useActionState>