React 19 新 API:并发特性与表单交互

Transition、useOptimistic、useFormStatus、useFormState——React 19 引入的核心 API 及其设计意图

React 19 在 RSC 之外,带来了一批面向用户交互的新 API。它们的共同目标是:让异步操作和表单交互有更好的 UX,同时不让开发者手动管理 loading/error 状态。


一、Transition:区分紧急与非紧急更新

问题

用户输入 → 触发搜索 → 结果列表更新。如果结果计算很慢,输入框也会卡顿——因为 React 默认把所有更新视为同等优先级。

解决

import { useTransition } from 'react'

function SearchBox() {
  const [isPending, startTransition] = useTransition()
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])

  function handleChange(e) {
    setQuery(e.target.value)  // 紧急:输入框立刻响应

    startTransition(() => {
      setResults(search(e.target.value))  // 非紧急:可以被打断
    })
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending ? <Spinner /> : <ResultList results={results} />}
    </>
  )
}

startTransition 内的更新优先级降低,可以被更紧急的更新(比如用户继续输入)打断。isPending 告诉你是否有挂起的 transition,用来显示 loading 状态。

React 19 的扩展

React 19 之前,startTransition 只支持同步函数。React 19 开始支持 async:

startTransition(async () => {
  const data = await fetchResults(query)
  setResults(data)
})

Transition 从“优先级调度”扩展到了“异步操作管理”。


二、useOptimistic:乐观更新的标准写法

问题

点赞按钮:用户点击 → 等待服务端响应 → 更新 UI。这个延迟让交互感觉迟钝。乐观更新的做法是先假设成功、立刻更新 UI,失败了再回滚。

以前要手动管理这套逻辑,容易出错。

解决

import { useOptimistic } from 'react'

function LikeButton({ postId, initialLikeCount }) {
  const [likeCount, setLikeCount] = useState(initialLikeCount)
  const [optimisticCount, addOptimistic] = useOptimistic(
    likeCount,
    (current, increment) => current + increment
  )

  async function handleLike() {
    addOptimistic(1)  // 立刻 +1
    try {
      await likePost(postId)
      setLikeCount(c => c + 1)  // 服务端确认后更新真实值
    } catch {
      // 自动回滚到 likeCount
    }
  }

  return (
    <button onClick={handleLike}>
      ♥ {optimisticCount}
    </button>
  )
}

useOptimistic 接受真实值和更新函数,返回乐观值。异步操作完成后自动同步回真实值,失败自动回滚。不需要手动写回滚逻辑。


三、useFormStatus:表单子组件感知提交状态

问题

Submit 按钮需要在表单提交时 disable 并显示 loading,但它通常是一个子组件,拿不到父级表单的提交状态。以前要靠 prop drilling 或 context 传递。

解决

import { useFormStatus } from 'react-dom'

function SubmitButton() {
  const { pending } = useFormStatus()
  return (
    <button disabled={pending}>
      {pending ? '提交中...' : '提交'}
    </button>
  )
}

function ContactForm() {
  return (
    <form action={submitAction}>
      <input name="email" type="email" />
      <SubmitButton />  {/* 自动感知表单状态,无需传 prop */}
    </form>
  )
}

useFormStatus 只能在 <form> 的子组件里用,自动订阅最近的父级表单状态。pending 在表单提交期间为 true

注意:这是 react-dom 的 hook,不是 react


四、useFormState / useActionState:管理表单 action 的结果

问题

表单提交 → 服务端 action → 返回结果(成功/错误信息)。如何把这个结果回传给客户端组件?

解决

import { useActionState } from 'react'  // React 19 重命名为 useActionState

async function submitForm(prevState, formData) {
  const email = formData.get('email')
  if (!email.includes('@')) {
    return { error: '邮箱格式不正确' }
  }
  await saveEmail(email)
  return { success: true }
}

function ContactForm() {
  const [state, formAction, isPending] = useActionState(submitForm, null)

  return (
    <form action={formAction}>
      <input name="email" type="email" />
      {state?.error && <p className="error">{state.error}</p>}
      {state?.success && <p>提交成功!</p>}
      <button disabled={isPending}>
        {isPending ? '提交中...' : '提交'}
      </button>
    </form>
  )
}

useActionState 接受 action 函数和初始状态,返回当前状态、包装后的 action 和 pending 状态。action 函数接收上一次的状态和 FormData,返回新状态。

React 19 将 useFormState(来自 react-dom)重命名为 useActionState(来自 react),同时新增了 isPending 返回值。


五、三个 API 的协作

实际场景里这三个 API 经常一起出现:

function CommentForm({ postId }) {
  const [state, formAction, isPending] = useActionState(
    submitComment,
    null
  )
  const [optimisticComments, addOptimistic] = useOptimistic(
    comments,
    (prev, newComment) => [...prev, newComment]
  )

  return (
    <>
      <CommentList comments={optimisticComments} />
      <form action={async (formData) => {
        addOptimistic({ text: formData.get('text'), pending: true })
        await formAction(formData)
      }}>
        <textarea name="text" />
        <SubmitButton />  {/* useFormStatus 在内部 */}
        {state?.error && <p>{state.error}</p>}
      </form>
    </>
  )
}
  • useActionState 管理服务端响应
  • useOptimistic 立刻更新 UI
  • useFormStatus 让 SubmitButton 自动感知状态

三者分工明确,各司其职。


核心结论

React 19 的这批 API 有一个共同设计原则:把异步操作的状态管理标准化,从组件里剥离出去。

以前要手动维护 isLoadingerroroptimisticValue 这些状态,逻辑分散容易出 bug。现在 React 内置了这些模式,让你专注于业务逻辑本身。

API 解决什么
useTransition 区分紧急/非紧急更新,async 支持
useOptimistic 乐观更新 + 自动回滚
useFormStatus 子组件感知表单提交状态
useActionState 管理 action 返回值和 pending 状态