译:React 19 计划推出的新 Hooks

原文:https://marmelab.com/blog/2024/01/23/react-19-new-hooks.html
作者:François Zaninotto
译者:ChatGPT 4 Turbo

编者注:一文帮你了解 React 19 的新 Hooks,感觉每个都很有用,但对于复杂场景来说又有点不够用,比如 use 代替不了 react query,比如 form action 代替不了 react hook form 等。

与大众认知相反,React 核心团队并不仅仅专注于 React Server Components 和 Next.js。在下一个主要版本 React 19 中,将会引入新的客户端钩子。它们主要针对 React 中的两大痛点:数据获取表单。这些 Hook 将提高所有 React 开发者的生产力,包括那些从事单页应用开发的开发者。

不多说了,让我们深入了解新的钩子吧!

  • use(Promise)
  • use(Context)
  • Form actions
  • useFormState
  • useFormStatus
  • useOptimistic
  • Bonus: Async transitions

注意:这些 Hook 函数仅在 React 的 Canary 和实验性频道中可用。它们应该会成为即将到来的 React 19 的一部分,但在最终发布之前,API 可能会发生变化。

use(Promise)

这个新的 Hook 是官方用于客户端 “suspending” 的 API。你可以传递一个 promise 给它,React 会在它 resolve 之前 suspend。基本语法,摘自 React use 文档,是:

import { use } from 'react';

function MessageComponent({ messagePromise }) {
    const message = use(messagePromise);
    // ...
}

这个好消息是这个 Hook 可以用于数据获取。这里有一个具体的例子,展示了在 mount 和点击按钮时的数据获取。代码中没有使用 useEffect

还记得 <Suspense> 文档中的这个警告

Suspense-enabled data fetching without the use of an opinionated framework is not yet supported.
不支持在没有使用固执框架的情况下启用 Suspense 的数据获取。

嗯,对于 React 19 来说,这已经不再适用了。

这个新的 use hook 具有隐藏的能力:不同于所有其他的 React 钩子,use 可以在循环和条件语句中调用,比如 if

这是否意味着我们不再需要使用像 TanStack Query 这样的第三方库来在客户端获取数据?这还有待观察,因为 TanStack Query 不仅仅是解决一个 Promise

但这是朝着正确方向迈出的重要一步,它将使基于 REST 或 GraphQL API 的单页应用程序的构建变得更加容易。我对这个新钩子感到非常兴奋!

use(Context)

相同的 use 钩子可以用来读取 React 上下文。它和 useContext 完全一样,除了它可以在循环和条件语句中调用,就像 if

import { use } from "react";
function HorizontalRule({ show }) {
  if (show) {
    const theme = use(ThemeContext);
    return <hr className={theme} />;
  }
  return false;
}

这将简化某些用例的组件层次结构,因为在循环或条件语句中读取上下文的唯一方法是将组件拆分为两部分。

它在性能方面也是一个巨大的进步,因为即使上下文发生了变化,你现在也可以有条件地跳过组件的重新渲染。

Form Actions

这个新功能允许你向 <form>action 属性传递一个函数。当表单提交时,React 会调用这个函数:

<form action={handleSubmit} />;

如果在 React 18 中添加了一个 <form action> prop,你会收到这个警告:

Warning: Invalid value for prop action on <form> tag. Either remove it from the element or pass a string or number value to keep it in the DOM.
警告:属性 action<form> 标签上的值无效。请从元素中移除它,或传递一个字符串或数字值以保留在 DOM 中。

在 React 19 中,你可以这样编写表单:

addToCart 函数不是服务器操作。它在客户端被调用,并且可以是一个异步函数。

这将极大简化在 React 中处理 AJAX 表单的操作,例如,在搜索表单中。但是,这可能还不足以完全摒弃像 React Hook Form 这样的第三方库,因为它不仅仅处理表单提交(还包括验证、副作用等)。

提示:你可能会在上面的例子中发现一些可用性问题(提交时未禁用提交按钮,缺少确认消息,购物车更新延迟)。幸运的是,更多的钩子即将推出以帮助解决这个用例。继续阅读!

useFormState

这个新 hook 旨在帮助上文描述的异步表单操作功能。调用 useFormState 来访问上一次提交表单时操作的返回值。

import { useFormState } from 'react-dom';
import { action } from './action';

function MyComponent() {
    const [state, formAction] = useFormState(action, null);
    // ...
    return <form action={formAction}>{/* ... */}</form>;
}

例如,这允许你显示表单操作返回的确认或错误消息:

注意: useFormState 必须从 react-dom 导入,而不是 react

useFormStatus

useFormStatus 可以让你知道父级 <form> 当前是否正在提交或已经提交成功。它可以在表单的子元素中被调用,并且它返回一个包含以下属性的对象:

const { pending, data, method, action } = useFormStatus();

您可以使用 data 属性来显示用户提交的数据。您还可以显示一个待处理状态,如下例中,在表单提交时按钮被禁用:

注意: useFormState 必须从 react-dom 导入,而不是 react 。另外,它只有在父表单使用上述的 action 属性时才有效。

useOptimistic

这个新 hook 让你在提交操作时能够乐观地更新用户界面。

import { useOptimistic } from 'react';

function AppContainer() {
    const [optimisticState, addOptimistic] = useOptimistic(
        state,
        // updateFn
        (currentState, optimisticValue) => {
            // merge and return new state
            // with optimistic value
        },
    );
}

在上面的购物车示例中,我们可以使用这个钩子在 AJAX 调用完成之前显示已添加新商品的购物车:

乐观地更新 UI 是提升网页应用用户体验的一个很好的方法。这个钩子在这种用例中非常有帮助。

Bonus: Async Transitions

React 的 Transition API 允许你在不阻塞 UI 的情况下更新状态。例如,它允许你在用户改变主意时取消之前的状态更改。

方法是用 startTransition 调用来包装状态变化。

function TabContainer() {
    const [isPending, startTransition] = useTransition();
    const [tab, setTab] = useState('about');

    function selectTab(nextTab) {
        // instead of setTab(nextTab), put the state change in a transition
        startTransition(() => {
            setTab(nextTab);
        });
    }
    // ...
}

以下示例展示了使用此 Transitions API 的标签导航。点击“Posts”,然后立即点击“Contact”。注意这中断了“Posts”的慢速渲染。“Contact”标签立即显示。因为这个状态更新被标记为一个过渡,一个慢速的重新渲染并没有 freeze 用户界面。

useTransition 钩子在 React 18.2 中已经可用。React 19 的新特性是你现在可以传递一个异步函数给 startTransition ,React 会等待该函数执行完毕后开始过渡。

这对于通过 AJAX 调用提交数据并在过渡中渲染结果很有用。过渡的待处理状态从异步数据提交开始。它已经在上面描述的表单操作功能中使用。这意味着 React 调用包裹在 startTransition 中的 <form action> 处理程序,因此它不会阻塞当前页面。

此功能尚未在 React 文档中记录,但您可以在 pull request 中了解更多信息。

结论

所有这些功能都可以在仅客户端的 React 应用程序中工作,例如在使用 Vite 打包的应用程序中。你不需要像 Next 或 Remix 这样的 SSR 框架就可以使用它们——尽管它们也可以与服务器集成的 React 应用程序一起工作。

这些功能使得在 React 中实现数据获取和表单变得更加容易。然而,要创造出良好的用户体验,需要整合所有这些钩子,这可能会很复杂。或者,你可以使用像 react-admin 这样的框架,其中内置了带有乐观更新的用户友好表单。

为什么这些功能会出现在 React 19 而不是 React 18.3?看起来不会有 18.3 版本的发布,因为这些功能包含了一些小的不兼容变更

React 19 什么时候发布?目前还没有确切的发布时间,但是这篇文章中提到的所有功能已经可以使用了。不过我还是不建议现在就使用它们——在生产环境中使用 canary 版本不是一个好主意(即使 Next.js 做了)。

很高兴看到 React 核心团队正在努力提升所有 React 开发者的开发体验,不仅仅是那些从事 SSR 应用的开发者。他们似乎也在倾听社区的反馈——数据获取和表单处理是非常常见的痛点

我期待在 React 的稳定版本中看到这些功能!