React 19 新特性集合

前言:https://juejin.cn/post/7337207433868197915

新 React 版本信息

伴随 React v19 Beta 的发布,React v18.3 也一并发布。

React v18.3相比最后一个 React v18 的版本 v18.2 ,v18.3 添加了一些警告提示,便于尽早发现问题,从而在升级 React v19 时更容易。

React 18.3 更新内容

React 18.3 相对于 18.2 增加了对废弃 API 的警告以及其他为 React 19 所需的更改。

  1. React
  • 允许向 this.refs 写入以支持字符串 ref 的代码模式转换。
  • 在 StrictMode 外部使用已废弃的 findDOMNode 时,将发出警告。
  • 对使用已废弃的测试工具方法时发出警告。
  • 在 StrictMode 外部使用已废弃的遗留 Context 时,将发出警告。
  • 在 StrictMode 外部使用已废弃的字符串 ref 时,将发出警告。
  • 对函数组件中使用已废弃的 defaultProps 发出警告。
  • 当在组件或元素中展开 key 时,将发出警告。
  • 从测试工具中使用 act 时,如果方式不当,将发出警告。
  1. React DOM
  2. 对使用已废弃的 unmountComponentAtNode 方法时发出警告。
  3. 对使用已废弃的 renderToStaticNodeStream 方法时发出警告。

React v18 发布后,带来了以并发特性为主的各种新 API ( startTransition / useDeferredValue 等 )、新运作模式、及 stream SSR 上的改进等,其相比 React v17 像是一个增量的升级版。

而 React v19 则不然,包含了 大量 细小的、或破坏性的变更(如document meta data 和asset loading)。

由于 React v18 canary 已迭代很久,本次更新中的很多内容属于历史已公布的内容,故不会重复做展开介绍。

React v19 中的新特性 – 概述
以下是 React 19 将具有的新特性的快速概述:

🤖 React 编译器react compiler:React 正在努力实现一个新的编译器。目前,Instagram 已经在利用这项技术,它将在未来版本的 React 中发布。

🔥 服务器组件react server component:经过多年的开发,React 引入了服务器组件的概念。您现在可以在 Next.js 中使用此功能。

💪 动作action:动作还将彻底改变我们与 DOM 元素的交互方式。

🌇 文档元数据document metadata:另一个急需改进的方面即将到来,使开发人员能够用更少的代码实现更多功能。

💼 资源加载asset loading:这将使资源能够在后台加载,从而改善应用程序的加载时间和用户体验。

⚙️ Web 组件:这特别令人着迷:React 代码现在将使我们能够集成 Web 组件。我对此发展非常激动,因为它将开启无数可能性。

🪝 增强型钩子:令人兴奋的新钩子即将问世,将彻底改变我们的编码体验。

  • 新的 use() 钩子
  • useFormStatus() 钩子
  • useFormState() 钩子
  • useOptimistic() 钩子

React 19 将解决 React 长期存在的挑战之一:过度重新渲染的问题。开发人员历来花费了大量时间来解决这个问题,这经常导致性能问题。

对导致重新渲染的代码进行持续追踪和优化工作一直是工程师们的重复任务。但是有了 React 19,这个问题将得到缓解。框架将自动处理重新渲染,简化开发流程。

以前,开发人员依赖 useMemo()、useCallback()、memo 等技术来管理重新渲染。但是在 React 19 中,这样的手动干预将不再必要。

框架将在幕后智能识别和记忆代码,从而产生更清晰和更有效的代码。 

React v20
目前已知内容:

  • React compiler (编译优化器,如 React forget )不等于 React v19 。
  • Activity ( 原 Offscreen )可能在 React v20 推出。

V19新特性介绍

1. React 编译器

这是为了性能做的更新。优化重新渲染的一种方式是手动使用 useMemo()、useCallback() 和 memo API。根据 React 团队的说法,这是一种“合理的手动妥协”。他们的愿景是让 React 管理这些重新渲染。但是 React 团队意识到手动优化很繁琐,而社区的反馈鼓励他们解决这个问题。因此,React 团队创建了“React 编译器”。React 编译器现在将管理这些重新渲染。React 将自动决定何时以及如何更改状态并更新 UI。

React Compiler,在开发者不调整任何代码的情况下,自动优化项目性能。帮助我们在不使用 memo/useMemo/useCallback 的情况下,方便的处理好项目 re-render 的问题。你的项目最终只会在需要更新的地方 re-render。React Compiler 编译之后,你的代码并不会改变现有渲染机制,而是在已有机制下完成对项目的优化。

React Compiler能够将React代码编译成更为优化的JavaScript代码。这一改变能够显著提升React应用的运行效率。编译器的引入不仅优化了代码的执行时间,还减少了浏览器的负载,从而使应用响应更快,交互更流畅。

与依赖追踪的细粒度更新不同,React Compiler 通过记忆的方式,让组件更新只发生在需要更新的组件,从而减少大量 re-render 的组件。

但是请注意,React Compiler 并非全能,如果你写的代码过于灵活,无法被提前预判执行行为,那么 React Compiler 将会跳过这一部分的优化。因此好的方式是在项目中引入严格模式,在严格模式的指导下完成的开发,基本都在 React Compiler 的辐射范围之内

例子1:在 React19 之后,不再需要使用 useMemo() 钩子,因为 React 编译器将自行进行记忆。

之前:

import React, { useState, useMemo } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  // 缓存检查输入值是否为空的结果
  const isInputEmpty = useMemo(() => {
    console.log('检查输入是否为空...');
    return inputValue.trim() === '';
  }, [inputValue]);

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="输入一些内容..."
      />
      <p>{isInputEmpty ? '输入为空' : '输入不为空'}</p>
    </div>
  );
}

export default ExampleComponent;
之后:不需要再使用 useMemo 缓存值了 - React19 将在底层自行处理。
import React, { useState } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  const isInputEmpty = () => {
    console.log('检查输入是否为空...');
    return inputValue.trim() === '';
  });

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="输入一些内容..."
      />
      <p>{isInputEmpty ? '输入为空' : '输入不为空'}</p>
    </div>
  );
}

export default ExampleComponent;

2. 服务器组件 

React Server Components 是 React 在探索前端边界的又一次突破性的创举。它是一种新概念组件。我们可以在构建时运行一次组件,以提高页面的渲染速度。

预渲染、增量渲染、流式传输等概念对提高大型复杂项目的用户体验有非常大的帮助。好消息是,RSC 已经在 Next.js 中得到落地实践。

React 19加强了对服务端组件(Server Components)的支持,这些组件在服务器上渲染完成后再发送到客户端,极大地提高了页面加载速度。

这一特性尤其适合内容重的网站应用,允许部分组件的逻辑在服务器上执行,而不会发送相关的JavaScript到客户端。这样做的好处包括减少了传输数据量,加快了首次加载速度,并优化了SEO。

举个例子:

// ServerUserInfo.react.server.js
// 注意:这个文件的扩展名为 .server.js,表示它是一个服务端组件。

import { db } from './database';

// 从数据库中获取用户信息的函数
async function fetchUserData(userId) {
  return db.query('SELECT * FROM users WHERE id = $1', [userId]);
}

function ServerUserInfo({ userId }) {
  const userData = fetchUserData(userId);
  return (
    <div>
      <h1>User Information</h1>
      <p>Name: {userData.name}</p>
      <p>Email: {userData.email}</p>
    </div>
  );
}

export default ServerUserInfo;

这样我们就创建了一个服务端组件,它直接从数据库获取用户信息,并在服务器上进行渲染。这样做还可以保护用户的敏感信息,因为这些数据不会被发送到客户端,同时增强了安全性,并且由于减少了客户端处理逻辑,页面加载速度更快。

在客户端中,我们只需要引用这个组件,并在构建时配置好相关的服务端渲染逻辑。

服务端组件带来的额优势:

  • SEO:服务器渲染的组件通过向网络爬虫提供更可访问的内容来增强搜索引擎优化。
  • 性能提升:服务器组件有助于更快地加载页面和改善整体性能,特别是对于内容密集型应用程序。
  • 服务器端执行:服务器组件使在服务器上执行代码变得无缝和高效,使诸如 API 调用之类的任务变得轻松。

目前 Next.js 支持服务器端组件。React 中所有组件默认都是客户端的。只有当你使用 ‘use server’ 时,组件才是服务器组件
可以在同一项目中的任何 React 组件中导入 requestUsername。在导入服务器组件后,我们可以使用“Actions”(我们很快会学习到)执行特定任务。

'use server';

export default async function requestUsername(formData) {
  const username = formData.get('username');
  if (canRequest(username)) {
    // ...
    return 'successful';
  }
  return 'failed';
}

3. 动作 action

submitData 是服务器组件中的动作。form 是一个客户端组件,它使用 submitData 作为动作。submitData 将在服务器上执行。客户端(form)和服务器(submitData)组件之间的通信只有通过动作属性才能实现。

"use server"

const submitData = async (userData) => {
    const newUser = {
        username: userData.get('username'),
        email: userData.get('email')
    }
    console.log(newUser)
}
const Form = () => {
    return <form action={submitData}>
        <div>
            <label>Name</label>
            <input type="text" name='username'/>
        </div>
        <div>
            <label>Name</label>
            <input type="text" name="email" />
        </div>
       
 <button type='submit'>Submit</button>
    </form>
}

export default Form;

相关新增的api:

新 API :useActionState
重要性:★

参考信息:useActionState 代码例子

此 API 原名为 useFormState ,主要用于联动原生 <form /> 表单提交,现代开发更多使用高级表单库,故此特性不重要。

新 API :useFormStatus
重要性:★

参考信息:useFormStatus 文档

此 API 与 useFormState 联动使用,现代开发更多使用高级表单库,故此特性不重要。

新 API :useOptimistic
重要性:★★

参考信息:useOptimistic 文档

通过此 API 派生其他的 state ,从而在提交动作时,立即乐观更新此值来优化用户视觉上的交互体验。

要实现乐观更新,必然要编写许多额外逻辑,同时现代请求库(如 react-query 、SWR 等)早已提供了乐观更新的功能。然而更多时候,我们没有精力和工时来提升用户体验,过度提升体验反而会造成更多冗余的乐观更新逻辑导致后续维护困难,展示 loading 已经足够,故此 API 不重要,若不是有心为之,则很难用到。

4. Web 组件

Web 组件允许你使用原生 HTML、CSS 和 JavaScript 创建自定义组件,并将它们无缝地整合到你的 Web 应用程序中,就像它们是标准 HTML 标签一样。

之前,在 React 中集成 Web 组件并不简单。通常情况下,要么需要将 Web 组件转换为 React 组件,要么安装额外的包并编写额外的代码,以使 Web 组件与 React 协同工作。

React 19改进了对Web组件的支持,使得在React项目中整合使用Web标准组件变得更加无缝。这使得开发者可以利用广泛的Web组件生态系统,而不必为了在React中使用它们而写大量的封装代码。

import React from 'react';

function CustomButtonComponent() {
  return (
    <>
      <my-button>Click Me!</my-button>
    </>
  );
}

export default CustomButtonComponent;

在这个示例中,<my-button>是一个自定义的Web组件,它可以直接在React中使用,无需任何特别的封装或桥接代码。这种整合提高了代码的复用性,并允许React开发者利用现有的Web组件库。

可能有的同学会说,这一个自定义的标签有什么用呢?回答是:非常有用。

因为在React 19的现代Web框架中使用Web组件时,<my-button>并不是一个普通的HTML标签。

它实际上是一个Web组件——这是一种使用自定义元素、Shadow DOM、HTML模板封装好的可重用组件。 当你在Web应用中使用像<my-button>这样的自定义元素时,浏览器会渲染出这个元素的定义所包含的所有样式和行为。

这些自定义元素的行为和外观完全由你定义的Web组件的JavaScript和CSS控制。以下假设定义了一个自定义的web组件:

class MyButton extends HTMLElement {
  constructor() {
    super(); // 调用父类的constructor
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <style>
        button {
          background-color: blue;
          color: white;
          padding: 10px 20px;
          border: none;
          border-radius: 5px;
          cursor: pointer;
        }
      </style>
      <button><slot></slot></button>
    `;
  }
}

// 定义新的自定义元素
customElements.define('my-button', MyButton);

5. 文档元数据 

对元数据的管理变得更加简单。React 19引入了可以直接在React组件中使用HTML元数据标签的能力,这样开发者就不再需要依赖如react-helmet这样的库来动态更新文档头部信息。

1)基本元数据标签

考虑到 SSR 的完备性,使用第三方库插入元信息仍然更好,所以此特性不重要。

之前:

import React, { useEffect } from 'react';

const HeadDocument = ({ title }) => {
  useEffect(() => {
    document.title = title;

 	const metaDescriptionTag = document.querySelector('meta[name="description"]');
    if (metaDescriptionTag) {
    metaDescriptionTag.setAttribute('content', 'New description');
    }
  }, [title]);

  return null;
};

export default HeadDocument;

 现在:在 React 中以前是不可能的。唯一的方法是使用像 react-helmet 这样的包。

function HomePage() {
  return (
    <>
      <title>主页 - 我的网站</title>
      <meta name="description" content="这是我的网站主页,提供各种服务信息。" />
      <h1>欢迎来到我的网站</h1>
      <p>这里有丰富的资源和信息。</p>
    </>
  );
}

在这个示例中,<title><meta>标签直接放置在组件返回的JSX中。

React 19可以处理这些标签,并将它们正确地渲染到HTML文档的头部。这种方式简化了元数据的管理,特别是在单页面应用中,可以更灵活地针对不同页面设置SEO相关的元数据。

2)样式表支持

参考信息:插入 link / meta / script / style / title 文档说明

对于使用 webpack 等打包工具进行模块化开发的现代,除 特别的样式覆盖 或 异步分包、构建工具缺陷 等原因导致对顺序出现 workaround 的需求外,一般我们在业务项目内不会接触到手动管理标签的情况,故此特性不重要

在Web开发中,样式表的管理至关重要,无论是通过外部链接(<link rel="stylesheet" href="...">)还是内嵌方式(<style>...</style>)引入,都需要在 DOM 中精准布局,以确保样式优先级得到妥善处理。然而,构建一个能够支持组件内部样式表组合的机制往往十分繁琐,因此开发者常常面临权衡:要么将样式远离其依赖的组件加载,牺牲组织性;要么依赖外部样式库,增加额外的复杂性。

React 19 针对这一挑战提供了内置支持,不仅简化了样式表的管理流程,还进一步增强了与客户端并发渲染和服务器端流式渲染的集成能力。通过指定样式表的优先级,React 将自动处理样式表在DOM中的插入顺序,确保在展示依赖于特定样式规则的内容之前,相关的样式表(无论外部还是内嵌)已经被加载并应用。

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会将样式表包含在<head>标签中,以确保浏览器在加载完成前不会进行页面绘制。如果在已经开始流式传输后才发现样式表,React 将确保在客户端将样式表插入到<head>标签中,然后再展示依赖于该样式表的Suspense边界的内容。

在客户端渲染过程中,React会等待新渲染的样式表加载完成后再提交渲染结果。如果在应用中的多个位置渲染了这个组件,React将确保样式表在文档中只被包含一次。

function App() {
  return <>
    <ComponentOne />
    ...
    <ComponentOne /> // 不会导致 DOM 中出现重复的样式表链接
  </>
}

对于那些习惯于手动加载样式表的开发者来说,React 19 的这一改进为他们提供了一个便利的机会。现在,可以将样式表直接放在依赖它们的组件旁边,这不仅有助于提升代码的可读性和可维护性,使得开发者可以更加清晰地了解每个组件的样式依赖关系,而且还能够确保只加载真正需要的样式表。

此外,样式库和与打包器集成的样式工具也可以采用这一新特性。即使开发者不直接渲染自己的样式表,只要他们所使用的工具升级到支持这一功能,他们同样能够享受到这一改进带来的好处。

 3)异步脚本支持

在HTML中,普通脚本(<script src="...">)和延迟脚本(<script defer src="...">)按照文档顺序加载,这限制了它们在组件树深处的灵活使用。然而,异步脚本能够以任意顺序加载,为开发者提供了更大的灵活性。

React 19 针对异步脚本提供了增强的支持,允许开发者在组件树的任何位置渲染它们,直接放在实际依赖该脚本的组件内部。这大大简化了脚本的管理,无需再担心脚本实例的重新定位和去重问题。

现在,你可以在组件中这样使用异步脚本:

function MyComponent() {  
  return (  
    <div>  
      <script async src="..." />  
      Hello World  
    </div>  
  );  
}  
  
function App() {  
  return (  
    <html>  
      <body>  
        <MyComponent />  
        ...  
        <MyComponent /> // 不会导致DOM中出现重复的脚本  
      </body>  
    </html>  
  );  
}

在所有渲染环境中,异步脚本都将进行去重处理,因此即使多个不同的组件渲染了同一个脚本,React 也只会加载和执行该脚本一次。

在服务端渲染中,异步脚本将被包含在<head>标签中,并优先于阻止绘制的更关键资源(如样式表、字体和图像预加载)之后加载。

6. 资源加载 

通常情况下,浏览器首先渲染视图,然后是样式表、字体和图像。这可能导致从未样式化(或未样式化内容的闪烁)到样式化视图的闪烁。

为了解决这个问题,开发人员通常会添加自定义代码来检测这些资源何时准备就绪,确保仅在一切加载完毕后显示视图。

在 React 19 中,图像和其他文件将在用户浏览当前页面时在后台加载。这个改进应该有助于改善页面加载时间并减少等待时间。

此外,React 还引入了用于资源加载的生命周期 Suspense,包括脚本、样式表和字体。这个特性使 React 能够确定内容何时准备好显示,消除了任何“未样式化”闪烁。

还有新的资源加载 API,如 preload 和 preinit,可以提供更大的控制权,使资源何时加载和初始化。

通过允许资源在后台异步加载,React 19 将最小化等待时间,并确保用户可以在没有中断的情况下与内容交互。这种优化不仅提升了 React 应用程序的性能,也为用户提供了更愉快的浏览体验。

7. 新的 React Hooks

React19 想要彻底改变我们在项目开发中的 UI 交互方式。因此对于 React19 而言,Suspense、ErrorBoundary、Action 的重要性将会变得越来越高。

React 19 之前,React 高手与普通开发者之间,开发的项目无论是从性能上、还是从代码优雅上差距都非常大。因此不同的人对于 React 的感受完全不一样。

而 React 19 则借由推出一些新的 hook,暗中传递一种框架思维「最佳实践」,这将会极大的拉进普通开发者与顶尖高手之间的差距。对于大多数 React 开发者而言,这会是一个极大的提升。

这一意图在 React 新的官方文档与 Next.js 中提现得非常明显

React 19在钩子函数方面也进行了增强,新增了use钩子。因此使用 useMemo、forwardRef、useEffect 和 useContext 的方式将发生变化。

这个钩子让我们在处理异步操作和资源获取更为便捷。通过use钩子,开发者可以在组件中直接挂载资源,如通过promise获取数据,React将暂停该组件的渲染直到数据加载完成,从而简化了代码结构并减少了不必要的渲染。

React19 的 大部分更新,几乎都是围绕如何在开发中尽量不用或者少用 useEffect 来展开。在之前的项目开发中,useEffect 是我们处理异步问题必须使用的重要 hook 之一,他几乎存在于每一个页面组件之中。

React 19 则引入了新的 hook,让我们处理异步开发时,不再需要 useEffect,从而极大的改变我们的开发方式。

使用 use 钩子处理异步数据
在React 19中,use 钩子可以让我们更简洁地处理异步数据。下面是一个使用use钩子来请求并展示用户数据的示例:

import { use } from 'react';  // 引入use钩子

function UserProfile({ userId }) {
  const userPromise = fetch(`https://api.example.com/users/${userId}`)
    .then(response => response.json());

  // 使用use钩子处理异步请求
  const user = use(userPromise);

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Location: {user.location}</p>
    </div>
  );
}

export default UserProfile;

use钩子允许组件在渲染过程中直接使用由 promise 返回的数据。当数据加载完成时,组件将重新渲染以显示数据。

对比旧的异步处理方式,在React 19之前,处理异步数据通常需要使用useState和useEffect钩子,如下例子所示:

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUser(data));
  }, [userId]);  // 依赖项数组中的userId确保当userId变化时重新发起请求

  if (!user) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Location: {user.location}</p>
    </div>
  );
}

export default UserProfile;

在这种传统方法中,我们需要管理一个状态变量user来存储用户数据,并使用useEffect来处理异步请求和响应。这不仅增加了代码量,也增加了组件的复杂度。

例子2:

在项目开发中,新页面渲染时请求一个接口的场景非常常见。新的架构思维的开发代码如下所示。

首先我们需要定义一个 API 用于请求数据。

const api = async () => {
  const res = await fetch('https://api.chucknorris.io/jokes/random')
  return res.json()
}

然后创建一个函数组件,并执行该 api

export default function Index() {
  const __api = api()
  return (
    <div>
      <div id='tips'>初始化时,自动获取一条数据内容</div>
      <Suspense fallback={<div>loading...</div>}>
        <Item api={__api} />
      </Suspense>
    </div>
  )
}

最后在子组件中,获取 api 执行之后得到的数据

const Item = (props) => {
  const joke = use(props.api)
  return (
    <div>
      <div>{joke.value}</div>
    </div>
  )
}

大家可以自行感受一下新的开发方式与以前基于 useEffect 请求数据有什么不同之处。

例子3:不再使用 useContext(),而是使用 use(context)。

import { createContext, useState, use } from 'react';

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

const Card = () => {
  // use Hook()
  const { theme, toggleTheme } = use(ThemeContext);

  return (
    <div
      className={`p-4 rounded-md ${
        theme === 'light' ? 'bg-white' : 'bg-gray-800'
      }`}
    >
      <h1
        className={`my-4 text-xl ${
          theme === 'light' ? 'text-gray-800' : 'text-white'
        }`}
      >
        Theme Card
      </h1>
      <p className={theme === 'light' ? 'text-gray-800' : 'text-white'}>
       Hello!! use() hook
      </p>
      <button
        onClick={toggleTheme}
        className='bg-blue-500 hover:bg-blue-600 text-white rounded-md mt-4 p-4'
      >
        {theme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'}
      </button>
    </div>
  );
};

const Theme = () => {
  return (
    <ThemeProvider>
      <Card />
    </ThemeProvider>
  );
};

export default Theme

 

8. 功能改进

1)ref 作为属性

ref 现在将作为 props 传递,而不是使用 forwardRef() 钩子。这将简化代码。因此,在 React19 之后,你将不再需要使用 forwardRef()。

之前:这是在 React 19 之前使用 forwardRef() 的示例:
import React, { forwardRef } from 'react';

const ExampleButton = forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));
之后:ref 可以作为 props 传递。不再需要 forwardRef()。
import React from 'react';

const ExampleButton = ({ ref, children }) => (
  <button ref={ref}>
    {children}
  </button>
);

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

2)作为提供者

React 19 允许直接将 <Context> 用作提供者,而无需使用传统的 <Context.Provider> 写法:

const ThemeContext = createContext('');  
  
function App({children}) {  
  return (  
    <ThemeContext value="dark">  
      {children}  
    </ThemeContext>  
  );  
}

这种新的语法更加简洁直观。为了方便开发者升级现有代码,React 团队将发布一个代码转换工具,能够自动将现有的 <Context.Provider> 转换为新的 <Context> 提供者。未来版本中,将逐步弃用 <Context.Provider>,以推动 React 社区向更加简化的语法过渡。

3)refs 清理函数

现在支持从 ref 回调函数中返回一个清理函数:

<input  
  ref={(ref) => {  
    // 创建 ref  
  
    // 新增:返回一个清理函数,当元素从 DOM 中移除时重置 ref。  
    return () => {  
      // ref 的清理工作  
    };  
  }}  
/>

当组件卸载时,React 将调用从 ref 回调函数中返回的清理函数。这适用于 DOM refs、类组件的 refs 以及 useImperativeHandle。

由于引入了 ref 清理函数的机制,现在 TypeScript 将拒绝从 ref 回调函数中返回除清理函数以外的任何内容。为了避免这个问题,我们通常建议避免使用隐式返回,比如将赋值操作放在花括号中,如下所示:

  • 原来的写法:
<div ref={current => (instance = current)} />
  • 优化后的写法:
<div ref={current => { instance = current; }} />

这种改变是因为 TypeScript 无法判断原始代码中返回的是否应该是清理函数,还是无意中的隐式返回值。通过将赋值操作明确地包裹在花括号中,确保了 ref 回调中不会意外地返回任何值,除非有意为之。

为了自动化这种模式的转换,可以使用 no-implicit-ref-callback-return 规则进行代码转换。这将帮助你在升级 React 版本时更顺畅地处理 ref 相关的代码。

4)useDeferredValue 的初始值

React 19 为 useDeferredValue 引入了 initialValue 选项,该选项允许指定组件首次渲染时返回的值。

function Search({ deferredValue }) {  
  // 在组件首次渲染时,返回 initialValue 作为 value。  
  // 随后,useDeferredValue 会在后台计划一次重渲染,使用 deferredValue 作为新的 value。  
  const value = useDeferredValue(deferredValue, { initialValue: '' });  
  
  return (  
    <Results query={value} />  
  );  
}

使用 initialValue 可以确保组件在首次渲染时能够立即显示一个占位值,而无需等待 deferredValue 的异步计算完成。随后,当 deferredValue 准备好时,useDeferredValue 会触发组件的后台重渲染,以显示最新的值。这有助于提升应用的响应性和用户体验。

5)资源预加载支持

新 API :preXXX 预加载资源

重要性:★★

参考信息:preconnect / prefetchDNS / preinit / preinitModule / preload / preloadModule 文档说明

在文档初始加载和客户端更新时,及时告知浏览器可能即将需要加载的资源,对于提升页面性能至关重要。React 19 引入了一系列新的API,旨在简化浏览器资源的加载和预加载过程,让开发者能够构建出流畅且高效的用户体验。

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';  
  
function MyComponent() {  
  preinit('https://.../path/to/some/script.js', { as: 'script' }); // 提前加载并执行脚本  
  preload('https://.../path/to/font.woff', { as: 'font' }); // 预加载字体  
  preload('https://.../path/to/stylesheet.css', { as: 'style' }); // 预加载样式表  
  prefetchDNS('https://...'); // 当可能会从该主机请求资源但尚不确定时  
  preconnect('https://...'); // 当确定会从该主机请求资源但不确定具体资源时  
}

这些 API 调用会在渲染组件时生成相应的DOM标签,如下所示:

<html>  
  <head>  
    <!-- 链接根据其对页面加载的贡献程度进行优先级排序 -->  
    <link rel="prefetch-dns" href="https://..."> <!-- DNS预获取 -->  
    <link rel="preconnect" href="https://..."> <!-- 提前建立连接 -->  
    <link rel="preload" as="font" href="https://.../path/to/font.woff"> <!-- 预加载字体 -->  
    <link rel="preload" as="style" href="https://.../path/to/stylesheet.css"> <!-- 预加载样式表 -->  
    <script async src="https://.../path/to/some/script.js"></script> <!-- 异步加载并执行脚本 -->  
  </head>  
  <body>  
    ...  
  </body>  
</html>

通过利用这些API,开发者可以优化页面的初始加载速度,减少用户等待时间。同时,在客户端更新时,预取和预加载资源也能帮助加快导航速度,提升用户体验。无论是通过预加载字体和样式表来减少页面渲染阻塞,还是通过预取DNS和预连接来加速资源请求,这些API都为开发者提供了强大的工具,使资源加载更加智能和高效。

分类来看:

  • preconnect 、prefetchDNS 等 API 将插入 <link rel="preconnect" /> 标签的行为命令化,但由于预加载时机越早越好,等到 React 应用运行后,已经错过了 preconnect 的最好时机,所以编写至 HTML 内或 SSR 仍然是首选方案,此类 API 不重要。
  • preinit / preload 等 API 将 脚本、样式 的加载命令化,但现代项目开发时已经内部模块化,使用 await import() 等懒加载行为已足够,故除了需要加载外部 脚本、样式 外,此 API 也不重要。

6)与第三方脚本和扩展的兼容性

在React 19中,对挂载过程进行了优化,以更好地兼容第三方脚本和浏览器扩展。

在客户端挂载时,如果渲染的元素与服务器返回的HTML中的元素不一致,React会触发客户端的重新渲染,以确保内容的正确性。然而,过去,若第三方脚本或浏览器扩展插入了某些元素,这会导致不匹配错误并触发不必要的客户端渲染。

现在,React 19 能够智能地跳过<head>和<body>中的意外标签,从而避免了因这些元素引发的不匹配错误。即使因为其他原因需要进行整个文档的重新渲染,React也会保留由第三方脚本和浏览器扩展插入的样式表,确保页面的完整性和一致性。

7)水合错误报告

React 19 在 react-dom 中对水合错误的报告进行了优化。过去,在开发模式下遇到水合不匹配时,系统往往只记录多个错误,而缺乏关于不匹配内容的具体信息。现在,引入了 diff 功能,使得客户端渲染与服务端渲染内容之间的差异一目了然。这一改进不仅提升了错误报告的清晰度,更有助于开发者迅速定位并修复水合相关问题,从而大幅提升开发效率。

现在只会记录一条包含不匹配内容差异的消息:

8)更好的错误报告

为了提供更细粒度的错误处理控制,还新增了两个根选项来与onRecoverableError相辅相成:

  • onCaughtError:当React在错误边界中成功捕获到错误时,此选项将被调用。
  • onUncaughtError:当错误被抛出且未能被任何错误边界捕获时,此选项将被触发。
  • onRecoverableError:当错误发生但React能够自动恢复时,该选项将起作用。

参考信息:onCaughtError / onUncaughtError / onRecoverableError 文档说明

这些新增选项不仅增强了React的错误处理能力,还赋予了开发者在不同错误场景下执行特定逻辑的能力。无论是进行错误日志的记录、发送错误报告,还是执行其他自定义操作,这些选项都能满足开发者的需求,帮助他们更有效地管理和应对React应用中的错误情况。

给 React 在全局 ReactDOM.createRoot 挂载时开放了一个全局捕获错误的出口:

ReactDOM.createRoot(document.getElementById('root')!, {
  onCaughtError: (error, errorInfo) => {
    // ...
  },
})

这可以避免在 ErrorBoundary 内部收集错误,而在全局更清真的统一处理(此处只是单纯报告错误,涉及到复杂的 ErrorBoundary 错误恢复和重试,仍然需要编写大量代码)。

注:不可以捕获 React 内的异步逻辑错误。

React 19 对错误处理进行了优化,旨在消除错误信息的重复,并为处理捕获和未捕获的错误提供了更多选项。例如,当渲染过程中发生错误并被错误边界捕获时,以前 React 会重复抛出相同的错误(一次是原始错误,另一次是在尝试自动恢复失败后),然后调用console.error输出错误发生位置的信息。

这种处理方式导致每个被捕获的错误都会被报告三次:

在 React 19 中将记录单个错误,并包含所有错误信息:


 

9)useRef 必须传入默认值
重要性:★★★

参考信息:useRef 入参更改说明

React v19 要求 useRef(defaultValue) 传入一个默认值,这会影响到所有历史 useRef() 未设定默认值的调用写法。

在以前,我们往往会通过 useRef<HTMLInputElement>(null!) 技巧 避免类型提示为空,因为此处 ref 必定存在,若存在默认值或采用技巧,则不存在此变动带来的问题,但相当一部分人并不会采用 React TypeScript 技巧 ,请多加留意。

10)React UMD 版本已被删除
重要性:★★★

参考信息:删除 UMD

仍有不少项目希望通过 external React UMD 的方式进行一些依赖外置化加载的设计,但未来 esm 是大势所趋,故 React v19 已删除 UMD 版本。

如还想通过外置形式使用 React ,需改为 esm 的 <script type="module"> 方式使用:

<script type="module">
  import React from "https://esm.sh/react@beta"
  import ReactDOM from "https://esm.sh/react-dom@beta/client"
  ...
</script>

各种不重要的小变化

API 相关
  • ReactDOM.render / ReactDOM.hydrate 旧版渲染应用 API 已被删除( 链接 )

  • useDeferredValue 支持设定默认值( 链接 )
  • React.createFactory 已被删除( 链接 )
  • react-test-renderer/shallow 导出位置已被更改( 链接 )
  • unmountComponentAtNode 旧版卸载节点 API 已被删除( 链接 )
  • ReactDOM.findDOMNode API 已被删除( 链接 )
特性相关
  • 更好的 web components 支持( 链接 )

注:这是一个较瞩目的新特性,但大多数人很少使用 web component ,故只有特定用户群体才值得去关注。

体验相关
  • 水合出现差异时,报错信息更友好( 链接 )。
  • 优化了控制台错误打印的格式( 链接 )
  • useLayoutEffect 在服务端运行的警告已被删除( 链接 )
Class 组件相关
  • Class 组件的 propTypes / defaultProps 已被删除( 链接 )
  • Class 组件的 getChildContext 已被删除( 链接 )
  • Class 组件的字符串 ref="string" 已被删除( 链接 )
函数组件相关
  • 函数组件的 render() 渲染方式已被删除( 链接 )
测试相关
  • act 函数导出位置已转移至 react ( 链接 )
  • react-test-renderer 已弃用( 非删除, 链接 )

注:此包以前可能用于一些简单的组件测试,但现在已被弃用,请在所有 React 的测试中统一使用 RTL ( @testing-library/react ),这将是官方推荐的唯一测试库。

类型相关
  • ReactElement 类型被调整( 链接 )
  • 限定 React JSX 类型的命名空间( 链接 )
  • 注:为了避免各种 JSX 的类型在全局冲突,Vue v3.4 也限制了自己的命名空间。
  • useReducer 类型改进( 链接 )

新增api参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/744679.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数学建模--Matlab操作与运算

目录 1.点运算 2.文件介绍 &#xff08;1&#xff09;文件分类 &#xff08;2&#xff09;温度转换 &#xff08;2&#xff09;函数调用 &#xff08;3&#xff09;建模经验 &#xff08;4&#xff09;注意事项 &#xff08;5&#xff09;多个返回值情况 &#xff08;6…

离线部署OpenIM

目录 1.提取相关安装包和镜像 2.安装docker和docker-compose 3.依次导入镜像 4.解压安装包 5.执行安装命令 6.PC Web 验证 7.开放端口 7.1IM 端口 7.2Chat 端口 7.3 PC Web 及管理后台前端资源端口 “如果您在解决类似问题时也遇到了困难&#xff0c;希望我的经验分享…

将深度相机的实时三维坐标数据保存为excel文档(Python+Pyrealsense2+YOLOv8)

一、如何将数据保存为excel文档 1.excel文件库与相关使用 &#xff08;1&#xff09;导入相应的excel文件库&#xff0c;导入前先要进行pip安装&#xff0c;pip install xlwt import xlwt # 导入用于创建和写入Excel文件的库 (2) 建立一个excel文档&#xff0c;并在第0行写…

Python | Leetcode Python题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:if not nums:return 0size len(nums)if size 1:return nums[0]first, second nums[0], max(nums[0], nums[1])for i in range(2, size):first, second second, max(first nu…

读AI新生:破解人机共存密码笔记12人工智能辩论

1. 言论 1.1. 对一个人终身职业的威胁&#xff0c;可能会使一个非常聪明的、通常很有思想的人说出一些话&#xff0c;但在进一步分析后&#xff0c;他们很可能希望收回这些话 1.2. 电子计算器在算术方面是“超人”&#xff0c;但是计算器并没有接管世界&#xff0c;因此&…

IMX6ULL SD卡启动uboot+kernel+rootfs

目录 1. 背景说明 2.SD卡启动 2.1准备条件 2.2 对SD卡分区格式化 2.3 制作sd卡镜像 3.效果测试 1. 背景说明 网络上绝大数教程&#xff0c;教大家把uboot烧录到SD卡&#xff0c;然后uboot启动后&#xff0c;通过TFTP下载kernel和设备树&#xff0c;然后通过nfs挂载文件系…

laravel的日志使用说明

文章目录 了解系统的默认支持多个通道时它们的关系如何使用驱动 了解系统的默认支持 Laravel 日志基于「 通道 」和 「 驱动 」的。那么这个通道是干嘛的&#xff1f;驱动又是干嘛的&#xff1f; 通道 &#xff1a; 1.它表示了某种日志格式化的方式&#xff08;或可理解为某个…

理解CNN模型如何学习

深度学习模型常常被认为是不可解释的。但是人们正在探索不同的技术来解释这些模型内发生了什么。对于图像&#xff0c;由卷积神经网络学习的特征是可解释的。我们将探索两种流行的技术来理解卷积神经网络。 可视化中间层的输出 可视化中间层的输出将有助于我们理解输入图像如何…

办公软件的答案?ONLYOFFICE 桌面应用编辑器会是最好用的 Office 软件?ONLYOFFICE 桌面编辑器使用初体验

文章目录 &#x1f4cb;前言&#x1f3af;什么是 ONLYOFFICE&#x1f3af; 主要功能介绍及 8.1 新功能体验&#x1f3af; 在线体验&#x1f4dd;最后 &#x1f4cb;前言 提到办公软件&#xff0c;大家最常用的可能就是微软的 Microsoft Office 和国产的 WPS Office。这两款软件…

使用API有效率地管理Dynadot域名,为文件夹中的域名进行域名停放

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

解锁高效办公:ONLYOFFICE新版本8.1功能揭秘与个人实战体验

文章目录 &#x1f4af;ONLYOFFICE 桌面编辑器 8.1 ✍1 新增功能介绍✍2 轻松编辑器PDF文件&#x1f353;2.1 PDF新增编辑器操作&#x1f353;2.2 PDF新增表单操作 ✍3 用幻灯片版式快速修改幻灯片✍4 无缝切换文档编辑、审阅和查看模式✍5 改进从右至左语言的支持 & 新的本…

C++——布隆过滤器

目录 布隆过滤器的提出 布隆过滤器的概念 布隆过滤器的基本原理和特点 布隆过滤器的实现 布隆过滤器的插入 布隆过滤器的查找 布隆过滤器的删除 布隆过滤器的优点 布隆过滤器的缺陷 布隆过滤器使用场景 布隆过滤器的提出 在注册账号设置昵称的时候&#xff0c;为了保证…

【已解决】SpringBoot图片更新需重启服务器才能显示

问题描述 1、更新头像&#xff0c;并跳转回列表页&#xff0c;发现显示不出来 2、但是前端获取用户头像的信息是在加载页面就会被调用的&#xff0c;同时前端也不存在所谓的缓存问题&#xff0c;因为没有动这部分代码。 但查看响应是能获得正确的信息&#xff08;前端打印图片…

Docker 下载与安装以及配置

安装yum工具 yum install -y yum-ulits配置yum源 阿里云源 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Docker 17.03后为两个版本&#xff1a; 社区版&#xff08;Community Edition&#xff0c;缩写为 CE&#x…

内网一键部署k8s-kubeshpere,1.22.12版本

1.引言 本文档旨在指导读者在内网环境中部署 Kubernetes 集群。Kubernetes 是一种用于自动化容器化应用程序部署、扩展和管理的开源平台&#xff0c;其在云原生应用开发和部署中具有广泛的应用。然而&#xff0c;由于一些安全或网络限制&#xff0c;一些组织可能选择在内部网络…

【踩坑】修复循环设置os.environ[‘CUDA_VISIBLE_DEVICES‘]无效

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 问题示例 for gpus in [0, 1, 2, 3, 4, 5, 6, 7]:os.environ[CUDA_VISIBLE_DEVICES] gpusprint(torch.cuda.get_device_name(0)) 始终将使用第…

专业技能篇---计算机网络

文章目录 前言计算机网络基础一、网络分层模型 HTTP一、从输入URL到页面显示发生了什么&#xff1f;二、Http的状态码有哪些&#xff1f;三、 HTTP与HTTPS有什么区别&#xff1f;四、URI 和 URL 的区别是什么?五、Cookie和Session有什么区别&#xff1f;六、GET与POST WebSock…

期货投机的操作

期货投机是一种高风险、高回报的投资方式&#xff0c;吸引着众多投资者参与。将深入探讨期货专业投机的操作秘诀&#xff0c;帮助投资者掌握必要的知识和技巧&#xff0c;在期货市场中驰骋。 一、期货专业投机的本质 期货投机是利用期货合约进行买卖&#xff0c;以赚取差价的一…

Diffusion Mamba:用于CT到MRI转换的Mamba扩散模型

Diffusion Mamba&#xff1a;用于CT到MRI转换的Mamba扩散模型 提出背景拆解左侧&#xff1a;整体框架中间&#xff1a;Mamba块的细节右侧&#xff1a;螺旋扫描的细节 提出背景 论文&#xff1a;https://arxiv.org/pdf/2406.15910 代码&#xff1a;https://github.com/wongzbb…

JAVA【案例5-2】模拟默认密码自动生成

【模拟默认密码自动生成】 1、案例描述 本案例要求编写一个程序&#xff0c;模拟默认密码的自动生成策略&#xff0c;手动输入用户名&#xff0c;根据用户名自动生成默认密码。在生成密码时&#xff0c;将用户名反转即为默认的密码。 2、案例目的 &#xff08;1&#xff09…