React 19 终于发布,一大波新功能正式升级

2024-12-06
24769 分钟
...

这篇文章主要介绍了 React 19 的新功能,包括 Actions 自动处理数据突变相关状态,新增的 useActionState、useFormStatus、useOptimistic 等 hook,新的 use API 读取渲染资源,新的 React DOM 静态 API 生成静态站点,以及 React 服务器组件中的 RSC 和 RSA 等内容。同时提到后续会分享针对旧版功能的优化和改良。

关联问题: React 19 性能如何 RSC 如何使用 Actions 适用场景

00. Hello World

大家好,我是大家的 林语冰

React 18 发布两年半后,React 团队终于官宣,React 19 稳定版正式升级。

01. React 19 新功能

1.1 Action

React 应用的常见用例之一是执行数据突变,然后更新状态作为响应。

比如,用户提交表单改名时,你会发出请求,然后处理响应。过去,你需要手动处理挂起状态、错误、乐观更新和串行请求。

举个栗子,以前可以在 useState 中处理挂起和错误状态:

function UpdateName({}) {
  
  const [isPendingsetIsPending= useState(false)

  const handleSubmit = async () => {
    setIsPending(true)
    const error = await updateName(name)
    setIsPending(false)
    if (error) {
      setError(error)
      return
    }
    redirect('/path')
  }
  
}

React 19 支持在转换中使用异步函数,自动处理挂起状态、错误、表单和乐观更新。

现在,可以使用 useTransition 来处理挂起状态:

function UpdateName({}) {
  
  const [isPending, startTransition] = useTransition()

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name)
      if (error) {
        setError(error)
        return
      }
      redirect('/path')
    })
  }
  
}

异步转换会立即将 isPending 的状态设置为 true,发出异步请求,并在任何转换后将 isPending 切换为 false

按照惯例,使用异步转换的函数称为“Actions”(动作)。

Actions 会自动管理提交的数据:

  • 挂起状态:Actions 提供挂起状态,该状态在请求开始时启动,并在提交最终状态更新时自动重置。
  • 乐观更新:Actions 新增 useOptimistic hook,你可以在提交请求时向用户显示即时反馈。
  • 错误处理:Actions 提供错误处理,在请求失败时显示错误边界,并自动将乐观更新恢复为其原始值。
  • 表单<form> 元素现在支持将函数传递给 actionformAction props。将函数传递给 action props 默认使用 Actions,并在提交后自动重置表单。

React 19 构建于 Actions 之上,引入 useOptimistic 管理乐观更新,并新增了 React.useActionState hook 来处理 Actions 的常见情况。

react-dom 添加了 <form> Actions 自动管理表单,以及 useFormStatus 来支持表单动作的常见情况。

React 19 可以简化上述例子:

function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get('name'))
      if (error) return error
      redirect('/path')
      return null
    },
    null
  )

  return <form action={submitAction}>// ...</form>
}

1.2 新 hook:useActionState

我们新增了 useActionState hook:

const [error, submitAction, isPending] = useActionState(
  async (previousState, newName) => {
    const error = await updateName(newName)
    if (error) {
      
      
      return error
    }
    
    return null
  },
  null
)

useActionState 接受一个 Action 函数,并返回一个包装 Action 来调用。

当调用包装 Action 时,useActionState 会返回 Action 的结果作为 data,并将其挂起状态返回为 pending

1.3 <form> Actions

React 19 还把 Actions 与 react-dom<form> 新功能集成,支持传递函数作为 <form><input><button> 元素的 actionformAction props,自动提交带有 Actions 的表单:

<form action={actionFunction}>

<form> Action 成功时,React 会自动重置非受控组件的表单。如果需要手动重置 <form>,可以调用新的 React DOM requestFormReset API。

1.4 新 hook:useFormStatus

在设计系统中,通常会编写需要访问其所在 <form> 信息的设计组件,而无需将 props 逐层传递到组件。

这可以通过 Context 来完成,但我们新增了一个简化 hook useFormStatus

import { useFormStatus } from 'react-dom'

function DesignButton() {
  const { pending } = useFormStatus()
  return <button type="submit" disabled={pending} />
}

useFormStatus 读取父级 <form> 的状态,就好像该表单是 Context provider 一样。

1.5 新 hook:useOptimistic

执行数据突变的另一种常见 UI 模式是在异步请求正在进行时乐观显示最终状态。

React 19 新增了一个 useOptimistic 简化 hook:

function ChangeName({ currentName, onUpdateName }) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName)

  const submitAction = async (formData) => {
    const newName = formData.get('name')
    setOptimisticName(newName)
    const updatedName = await updateName(newName)
    onUpdateName(updatedName)
  }

  return (
    <form action={submitAction}>
      <p>{optimisticName}</p>
      <input
        name="name"
        disabled={currentName !== optimisticName}
      />
    </form>
  )
}

updateName 请求正在进行时,useOptimistic hook 会立即渲染 optimisticName。当更新完成或出错时,React 会自动切换回 currentName 的值。

1.6 新 API:use

React 19 新增 use API 来读取渲染中的资源。

举个栗子,你可以使用 use 读取 promise 对象,React 会挂起直到该 promise 解析:

import { use } from 'react'

function Comments({ commentsPromise }) {
  
  const comments = use(commentsPromise)
  return comments.map((comment) => <p key={comment.id}>{comment}</p>)
}

function Page({ commentsPromise }) {
  
  
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

use 不支持渲染中创建的 promise 对象。如果你将渲染中创建的 promise 传递给 use,React 会报错。

你还可以使用 use 条件式读取 context,比如在提前 return 之后:

import { use } from 'react'
import ThemeContext from './ThemeContext'

function Heading({ children }) {
  if (children == null) {
    return null
  }
  
  
  const theme = use(ThemeContext)
  return <h1 style={{ color: theme.color }}>{children}</h1>
}

类似于 hooks,use API 只能在渲染中调用。与 hooks 不同的是,use 可以条件调用。

02. 新的 React DOM 静态 API

react-dom/static 新增了两个 API 来生成静态站点:

  • prerender
  • prerenderToNodeStream

这些新 API 通过等待数据加载以生成静态 HTML 来改进 renderToString,为了与 Node Streams 和 Web Streams 等流环境配合使用。

举个栗子,在 Web Stream 环境中,你可以使用 prerender 将 React 树预渲染为静态 HTML:

import { prerender } from 'react-dom/static'

async function handler(request) {
  const { prelude } = await prerender(<App />, {
    bootstrapScripts: ['/main.js'],
  })
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  })
}

prerender API 会等待所有数据加载,然后再返回静态 HTML 流。

流可以转换为字符串,或者与流响应一起发送。它们在加载时不支持流内容,而现有的 React DOM 服务端渲染 API 支持流内容。

03. React 服务器组件

3.1 RSC(服务器组件)

RSC[2] 是一个新选项,允许在打包之前在与客户端应用或 SSR 服务器分开的环境中提前渲染组件。这个单独的环境是 RSC 中的“服务器”。

RSC 可以在构建时在 CI 服务器上运行一次,也可以使用 Web 服务器针对每个请求运行。

3.2 RSA(服务器动作)

RSA[3] 允许客户端组件调用在服务器上执行的异步函数。

当使用 "use server" 指令定义 RSA 时,你的框架会自动创建对服务器函数的引用,并将该引用传递给客户端组件。当客户端调用该函数时,React 将向服务器发送请求来执行该函数,并返回结果。

一个常见的误解是使用 "use server" 来表示 RSC,但其实没有针对 RSC 的指令。"use server" 指令只适用于 RSA。

RSA 可以在 RSC 中创建,并作为 props 传递给客户端组件,也可以在客户端组件中导入和使用。

高潮总结

React 19 是 React 18 时隔两年后发布的主版本升级,涉及的新功能较多,RSC 等部分技术细节需要大家进一步阅读文档。

参考文献

[1] React 官方博客: react.dev/blog/2024/1…

[2] RSC: react.dev/reference/r…

[3] RSA: react.dev/reference/r…

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
react2025-08-03
在 React 中实现倒计时功能会有什么坑
倒计时 倒计时是一个非常常见的业务场景,但是在 React 中实现起来,却不算简单。 首先我们来看这段倒计时代码,它能否正常执行? 来看看实际表现效果 可以看到计时器在不断执行,但是 的值却没有变。 这是一个很经典的问题:React 闭包陷阱 。 我们来详细分析下: 的依赖数组 是空的,这意味着 effect 只会在组件挂载时执行一次,而不会在 状态更新时重新执行。所以 回调函数中捕获的 值始终是初始值 。 这个问题解决起来也很简单,有...
学习面试
react2025-07-30
React性能优化三剑客:memo、useMemo和useCallback详解
前言 在React开发中,性能优化是一个永恒的话题。今天我们就来深入探讨React提供的三个重要性能优化工具: 、 和 ,它们如何帮助我们构建更高效的React应用。 1\. 为什么需要性能优化? 🤔 React的核心机制是当组件的state或props发生变化时,组件会重新渲染。但有时这种重新渲染是不必要的: 父组件更新导致所有子组件重新渲染 ,即使子组件的props没有变化 复杂计算在每次渲染时重复执行 ,消耗大量资源 函数引用在...
学习面试
react2024-09-18
React | 浏览器刷新,会执行 useEffect 的 卸载函数吗?
React | 浏览器刷新,会执行 useEffect 的 卸载函数吗? 需求描述 后端在跑一个长期的异步上传任务,在任务执行过程中,用户刷新页面(f5),此时产品希望能够发送一个 kill 任务,杀掉这个任务。 拆解 需求合理性 其他情况是否存在类似:比如关闭 tab,关闭浏览器,突然断网等操作,等浏览器层级或者异常的用户行为 前端是否应该响应这种行为:对于更深层次的需求处理 根本问题点,或者说为什么会存在这个需求:上一个任务会加锁,...
学习面试
react2024-07-29
Effect:渲染之外的副作用
React 借鉴了许多函数式编程的理念,使得它在构建复杂用户界面时能够保持代码的简洁、可维护和可测试性。函数式编程在很大程度上依赖于纯函数 纯函数(Pure Function)是指在计算机科学和函数式编程中使用的一类函数。一个函数如果具备以下两个主要特性,我们就可以称之为纯函数: 1. 引用透明性:纯函数在相同的输入下总是产生相同的输出。也就是说,函数的输出仅依赖于其输入参数,不依赖于任何外部状态或变量。 2. 无副作用:纯函数不会改变...
学习面试
nuxt2025-10-22
Nuxt SSR 与 Next.js SSR
🧭 引言 在现代前端生态中, Nuxt(Vue) 与 Next.js(React) 是最成熟的两大全栈框架。 它们都具备 SSR(Server Side Rendering) 能力,但实现原理与运行机制差异巨大。 如果你在使用 Nuxt 构建 SSR 项目,却发现“路由跳转后不再 SSR”, 而在 Next.js 中同样操作却会重新发起服务端渲染——这不是 bug,而是 理念差异 。 本文将详细对比这两种框架的 SSR 行为,帮助你彻...
学习面试
vue2025-10-22
shallowRef 与 ref 的区别、场景与坑位
结论先行: 会 深度 地把你放进去的对象转成响应式(递归代理),因此"对象内部的改动"也会触发视图更新。 只在 顶层 做依赖追踪: 更换 才触发更新 ;若你只是"在原对象上改属性",不会触发,除非手动 。 1\. 快速对比 维度 追踪粒度 深度(递归) 仅顶层 对象内部属性变更 会触发更新 不会 触发(除非 或重新赋值) 性能 有递归 & 依赖跟踪开销 更轻量,适合大对象/第三方实例 适用对象 普通标量、普通对象、数组 第三方实例(图表...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录