React 19 测试版发布,时隔两年终于更新了

众所周知,前端开发这个领域日新月异,每隔一段时间就会整出一点新玩意,被戏称做“前端娱乐圈”,圈子里大佬一个比一个卷,都在疯狂的造轮子。

然而有这么一位选手,自从 2022 年 6 月发布了最后一次稳定版本后,就一直没有再发布新的版本,它就是大名鼎鼎的前端三大框架之一 React。

就在昨日,时隔将近两年,React 突然宣布,React 19 测试版现已在 npm 上发布,随之而来的还有 React 18.3.0 稳定版。

下面让我们一起来看看 React 19 测试版有什么新特性吧!

React 19 的新特性

Actions

在 React 应用中,执行数据变更然后更新状态是一个常见的用例。例如,当用户提交表单来更改他们的名字时,你将发起一个 API 请求,然后处理响应。在过去,你需要手动处理待处理状态、错误、乐观更新和顺序请求。

例如,你可以在 useState 中处理待处理和错误状态:

// 动作之前
function UpdateName({}) {
  const [name, setName] = useState(" ");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

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

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        更新
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

在 React 19 中,我们增加了对在转换中使用异步函数以自动处理待处理状态、错误、表单和乐观更新的支持。

例如,你可以使用 useTransition 来为你处理待处理状态:

// 使用动作中的待处理状态
function UpdateName({}) {
  const [name, setName] = useState(" ");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

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

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        更新
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

异步转换将立即将 isPending 状态设置为 true,发起异步请求,并在任何转换后将 isPending 设置为 false。这允许你在数据变更时保持当前 UI 的响应性和交互性。

按照惯例,使用异步转换的函数被称为 Actions。Actions 自动为你提交数据:

  • 待处理状态:动作提供了一个从请求开始就启动的待处理状态,并在最终状态更新提交时自动重置。
  • 乐观更新:动作支持新的 useOptimistic 钩子,因此在请求提交时你可以向用户显示即时反馈。
  • 错误处理:动作提供错误处理,因此当请求失败时,你可以显示错误边界,并自动将乐观更新回退到它们的原始值。
  • 表单<form> 元素现在支持将函数传递给 actionformAction 属性。将函数传递给 action 属性默认使用动作,并在提交后自动重置表单。

在 Actions 的基础上,React 19 引入了 useOptimistic 来管理乐观更新,以及一个新的钩子 React.useActionState 来处理动作的常见情况。在 react-dom 中,我们增加了 <form> 动作来自动管理表单和 useFormStatus 来支持表单中动作的常见情况。

在 React 19 中,上述示例可以简化为:

// 使用 <form> 动作和 useActionState
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 (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>更新</button>
      {error && <p>{error}</p>}
    </form>
  );
}

在下一节中,我们将分解 React 19 中每个新动作特性的细节。

新钩子:useActionState

为了使动作的常见情况更容易,我们添加了一个名为 useActionState 的新钩子:

const [error, submitAction, isPending] = useActionState(async (previousState, newName) => {
  const {error} = await updateName(newName);
  if (!error) {
    // 你可以返回动作的任何结果。
    // 在这里,我们只返回错误。
    return error;
  }
  
  // 处理成功
});

useActionState 接受一个函数(“动作”),并返回一个包装后的动作来调用。这是因为动作可以组合。当包装后的动作被调用时,useActionState 将返回动作的最后一个结果作为 data,并将动作的待处理状态作为 pending

React.useActionState 之前在 Canary 版本中被称为 ReactDOM.useFormState,但我们已经重命名了它并弃用了 useFormState

React DOM:<form> 动作

动作还与 React 19 的新 <form> 特性集成,用于 react-dom。我们增加了支持将函数作为 <form><input><button> 元素的 actionformAction 属性传递,以自动使用动作提交表单:

<form action={actionFunction}>

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

React DOM:新钩子:useFormStatus

在设计系统中,编写需要访问其所在的 <form> 的信息的设计组件是很常见的,而不需要将属性钻取到组件中。这可以通过上下文完成,但为了使常见情况更容易,我们添加了一个新的钩子 useFormStatus

import {useFormStatus} from 'react-dom';

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

useFormStatus 读取父 <form> 的状态,就像表单是一个上下文提供者一样。

新钩子:useOptimistic

执行数据变更时,另一个常见的 UI 模式是在异步请求进行中乐观地显示最终状态。在 React 19 中,我们添加了一个新的钩子 useOptimistic 来使这变得更容易:

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>
      <p>
        <label>更改姓名:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

useOptimistic 钩子将在 updateName 请求进行中时立即渲染 optimisticName。当更新完成或出现错误时,React 将自动切换回 currentName 值。

新 API:use

在 React 19 中,我们引入了一个新的 API 来在渲染时读取资源:use

例如,你可以使用 use 读取一个 promise,React 将会在 promise 解决之前挂起:

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` 将会在 promise 解决之前挂起。
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // 当 `use` 在 Comments 中挂起时,
  // 这个 Suspense 边界将会被显示。
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

use 不支持在渲染中创建的 promises,如果你尝试将一个在渲染中创建的 promise 传递给 use,React 将会警告:

一个组件被一个未缓存的 promise 挂起了。在客户端组件或钩子中创建 promises 目前还不支持,除非是通过一个与 Suspense 兼容的库或框架。

要修复这个问题,你需要从支持缓存的 suspense 驱动的库或框架中传递一个 promise。在未来,我们计划推出一些功能,使在渲染中缓存 promises 更加容易。

你也可以使用 use 读取上下文,允许你有条件地读取 Context,例如在提前返回之后:

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

function Heading({children}) {
  if (children == null) {
    return null;
  }
  
  // 这在 useContext 中是行不通的
  // 因为提前返回了。
  const theme = use(ThemeContext);
  return (
    <h1 style={{color: theme.color}}>
      {children}
    </h1>
  );
}

use API 只能在渲染中调用,类似于钩子。与钩子不同,use 可以有条件地被调用。在未来,我们计划支持更多在渲染中使用资源的方式与 use

React 服务器组件

服务器组件

服务器组件是一个新的选项,允许在与您的客户端应用程序或服务器端渲染(SSR)服务器不同的环境提前进行组件渲染,即在构建之前进行渲染。这个独立的环境就是 React 服务器组件中的“服务器”。服务器组件可以在您的 CI 服务器上构建时运行一次,或者它们可以针对每个请求使用 Web 服务器运行。

React 19 包含了 Canary 频道中包含的所有 React 服务器组件特性。这意味着带有服务器组件的库现在可以将 React 19 作为具有 react-server 导出条件的同级依赖,用于支持全栈 React 架构的框架。

虽然 React 19 中的 React 服务器组件是稳定的,并且在主要版本之间不会破坏,但用于实现 React 服务器组件打包器或框架的底层 API 不遵循 semver,并且在 React 19.x 的次要版本之间可能会破坏。

要作为打包器或框架支持 React 服务器组件,我们建议固定到特定的 React 版本,或使用 Canary 版本。我们将继续与打包器和框架合作,以在未来稳定用于实现 React 服务器组件的 API。

服务器 Actions

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

当一个服务器 Actions 被定义为带有 "use server" 指令时,您的框架将自动创建对服务器函数的引用,并将该引用传递给客户端组件。当客户端上调用该函数时,React 会向服务器发送一个请求以执行该函数,并返回结果。

一个常见的误解是服务器组件由 "use server" 表示,但实际上没有用于服务器组件的指令。"use server" 指令用于服务器操作。

服务器 Actions 可以在服务器组件中创建,并作为属性传递给客户端组件,或者它们可以被导入并在客户端组件中使用。

有关更多 React 19 的改进功能,请参阅 React 官方博客原文:https://react.dev/blog/2024/04/25/react-19

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值