前端开发者学堂 - fedev.cn

React 19 新特性全面解析:升级你的 React 应用!

发布于 大漠

React 19 现已稳定发布!自从 2024 年 4 月 React 19 RC 发布以来,加入了一些新特性和改进,已可通过 npm 或 yarn 安装更新!

// 使用 npm
npm install --save-exact react@^19.0.0 react-dom@^19.0.0

// 使用 yarn
yarn add --exact react@^19.0.0 react-dom@^19.0.0

// 如果你是使用 TypeScript,还需要更新一下类型
npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0

yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0

React 19 中的新增功能与改进

异步状态管理

在 React 应用中,一个常见的场景是进行数据变更,并在数据变化后更新状态。比如,用户提交表单修改姓名时,通常需要发起 API 请求,并处理请求结果。在过去,你需要手动处理请求的挂起状态、错误、乐观更新等。

// Before Actions
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}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

在 React 19 中,我们为过渡(transition)增加了对异步函数的支持,可以自动处理挂起状态、错误、表单以及乐观更新。例如,使用 [useTransition]来自动管理请求的挂起状态。

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    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}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

Actions 现在能够自动管理这些状态。它们不仅支持挂起状态,还支持乐观更新和错误处理,简化了开发过程。

在 React 19 中,上面的例子可以简化为:

// Using <form> Actions and 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 null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

新的 hook:useActionState

为了简化 Action 的常见用法,React 19 引入了新的 hook——useActionState。该 hook 允许你管理 Action 的状态(如挂起状态、错误等),并返回最终的结果。

const [error, submitAction, isPending] = useActionState(
  async (previousState, newName) => {
    const error = await updateName(newName);
    if (error) {
      // You can return any result of the action.
      // Here, we return only the error.
      return error;
    }

    // handle success
    return null;
  },
  null,
);

React DOM 新特性:<form> 和 [useFormStatus]

React 19 将 [useFormStatus] 引入到 <form> 组件中,便于组件在不需要层层传递 props 的情况下,访问表单的状态。此外,还可以通过 <form> 自动管理表单的提交和重置操作。

<form action={actionFunction}>

乐观更新:useOptimistic

在进行数据变更时,常常需要在请求完成之前乐观地展示最终状态。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>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

新增 API:use

在 React 19 中,我们推出了 [use] API,它允许你在渲染中读取资源(如 Promise)。React 会在资源解析完成之前暂停渲染,确保在数据准备好后再进行渲染。

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` will suspend until the promise resolves.
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // When `use` suspends in Comments,
  // this Suspense boundary will be shown.
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

静态 API:prerender 和 prerenderToNodeStream

React 19 新增了两个用于静态网站生成的 API,改进了 renderToString,可以在静态 HTML 生成时等待数据加载,特别适合流式渲染环境,如 Node.js Streams 和 Web Streams。

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' },
  });
}

React Server 组件

React 19 支持 React Server 组件,可以将一些组件在构建时预渲染或按需在服务器端运行,从而提高性能。这个功能使得全栈 React 架构得以支持并得到了稳定版本的引入。

React 19 改进亮点

ref 作为 prop

function MyInput({placeholder, ref}) {
  return <input placeholder={placeholder} ref={ref} />
}

//...
<MyInput ref={ref} />

从 React 19 开始,函数组件可以直接使用 ref 作为 prop,而不再需要 forwardRef。未来版本将会弃用 forwardRef

优化错误报告

React 19 改进了 hydration 错误的报告方式。现在,如果客户端渲染与服务器端渲染不匹配,React 会记录单一的错误信息,而不再重复抛出错误。

样式表支持

React 19 在样式表的处理上进行了优化,支持更好地管理样式表的加载顺序,避免样式表加载过慢导致页面渲染延迟。

function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo" precedence="default" />
      <link rel="stylesheet" href="bar" precedence="high" />
      <article class="foo-class bar-class">
        {...}
      </article>
    </Suspense>
  )
}

function ComponentTwo() {
  return (
    <div>
      <p>{...}</p>
      <link rel="stylesheet" href="baz" precedence="default" />  <-- will be inserted between foo & bar
    </div>
  )
}

异步脚本支持

React 19 改善了异步脚本的渲染体验,支持在组件树中的任意位置渲染 async 脚本,并确保脚本不会重复加载。

function MyComponent() {
  return (
    <div>
      <script async={true} src="..." />
      Hello World
    </div>
  )
}

function App() {
  <html>
    <body>
      <MyComponent>
      ...
      <MyComponent> // won't lead to duplicate script in the DOM
    </body>
  </html>
}

资源预加载支持

React 19 引入了新的资源预加载 API,优化了初次加载和客户端更新的性能,能够提前加载需要的资源,提升页面响应速度。

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
  preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly
  preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
  preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
  prefetchDNS('https://...') // when you may not actually request anything from this host
  preconnect('https://...') // when you will request something but aren't sure what
}

兼容第三方脚本和扩展

通过改进 hydration 机制,React 19 可以更好地处理第三方脚本和浏览器扩展,避免它们引起的匹配错误。

更强大的错误处理

React 19 改进了错误处理机制,增加了 onCaughtErroronUncaughtError 和 [onRecoverableError]事件,以便开发者更灵活地处理不同类型的错误。

这个版本的 React 19 带来了许多令人兴奋的新特性与改进,无论是开发者体验,还是性能优化,都大大提升了 React 应用的效率和易用性。赶紧尝试升级你的项目吧!