• 详解 React 18 更新后的特性,一文即懂


    React 18 通过其改进的渲染系统带来了并发能力,并在此基础上构建了转换或自动批处理等性能增强特性。下面就看看到底有哪些值得关注的新特性。
    在这里插入图片描述

    迭代更新内容

    总的来说,由于新的并发特性是渐进适配并按需启用的,React 18 中的重大更改仅限于几个简单的 API 更改,以及对 React 中多个行为的稳定性和一致性的一些改进,比较重要的一点是,不再支持 IE 浏览器。

    1、客户端渲染 API

    带有 createRoot() 的 root API,替换现有的 render() 函数,提供更好的人体工程学并启用新的并发渲染特性。

    import { createRoot } from "react-dom/client";
    import App from "App";
    
    const container = document.getElementById("app");
    const root = createRoot(container);
    root.render(<App />);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    请注意,这个新的 API 现在已从 react-dom/client 模块导出,卸载和水合 API 也发生了变化。

    // Unmount component at DOM node:
    // ...
    root.unmount();
    
    // Hydration
    import { hydrateRoot } from "react-dom/client";
    // ...
    
    const container = document.getElementById("app");
    const root = hydrateRoot(container, <App tab="home" />);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由于使用 Suspense 时会出现不正确 timing 的问题,渲染回调已经一去不复返了。替代选择是顶部组件内部的一个效果:

    import { createRoot } from "react-dom/client";
    import { useEffect } from "react";
    import App from "App";
    
    const App = () => {
      useEffect(() => {
        console.log("render callback");
      });
    
      return <div></div>;
    };
    const container = document.getElementById("app");
    const root = createRoot(container);
    root.render(<App />);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、自动批处理

    createRoot() API 还是 React 18 中另一个改进的入口——自动批处理。在 React 的早期版本中,状态更新在 React 事件侦听器中完成时已经批量处理了,以优化性能并避免重渲染。从 React 18 开始,状态更新也将被安排到其他地方——比如在 Promise、setTimeout 回调和原生事件处理程序中。

    const App = () => {
      const handleClick = () => {
        setA((a) => a + 1);
        setB((b) => b - 1);
        // Updates batched - single re-render
      };
    
      setTimeout(() => {
        setA((a) => a + 1);
        setB((b) => b - 1);
        // New (v18): Updates batched - single re-render
      }, 1000);
    
      // ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个更改虽然一般来说符合人们期望,也挺有用,但可能是破坏性的。如果你的代码依赖于在分开的状态更新之间重渲染的组件,那么你必须使其适应新的批处理机制,或使用 flushSync() 函数来强制立即刷新更改。

    import { flushSync } from "react-dom";
    // ...
    
    const handleClick = () => {
      flushSync(() => {
        setA((a) => a + 1);
      });
      // Re-render
      flushSync(() => {
        setB((b) => b - 1);
      });
      // Re-render
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3、严格模式更新

    React 18 带来了大把新特性,此外还有很多新特性正在路上。为了让你的代码为此做好准备,StrictMode 变得更加严格了。最重要的是,StrictMode 将测试组件对可重用状态的弹性,模拟一系列的挂载和卸载行为。它旨在让你的代码为即将推出的特性(可能以组件的形式)做好准备,这将在组件的挂载周期中保留这个状态。

    虽然它肯定会在未来提供更好的性能,但就目前而言,启用 StrictMode 时必须要考虑这个事情。

    除了以上提到的更改之外,根据你的 React 代码库,你可能还会发现其他一些更改。

    值得一提的是,React 18 将不再支持 IE 浏览器,因为 React 18 现在依赖很多现代浏览器特性,如 Promise 或 Object.assign。

    其余的更改与一些 React 行为的稳定性和一致性有关,不太可能影响你的代码库。

    4、并发的 React

    并发渲染器是 React 渲染系统的一项幕后特性。它允许并发渲染,即同时在后台准备多个版本的 UI。这意味着更好的性能和更平滑的状态转换。

    虽然并发似乎只是一个实现细节,但其实它是大多数新特性的动力源泉。事实上,只有当你使用其中一种特性(如 transition、Suspense 或流式 SSR)时,才会启用并发渲染。这就是为什么了解并发渲染的工作机制是非常重要的。

    5、Transition

    Transition 是由并发渲染提供支持的新特性之一。它旨在与现有状态管理 API 一起使用,以区分紧急和非紧急状态更新。通过这种方式,React 知道哪些更新需要优先考虑,哪些更新需要在后台通过并发渲染准备。

    要知道何时使用 transition,你必须更好地了解用户是如何与你的应交互的。例如,在字段中键入或单击按钮是用户期望立即获得响应的操作——响应可能是出现在文本字段中的一个值,或是要打开的某个菜单。但对于搜索、加载或处理数据(例如搜索栏、图表、过滤表等)这些事情,用户也会期望它们需要一些时间来完成。后者就是你使用 transition 的场景了。

    可以使用 useTransition() 钩子来创建一个 transition。这个钩子返回一个函数来启动一个 transition,还有一个挂起的指示器来通知你 transition 的进度。

    import { useTransition, useState } from "react";
    
    const App = () => {
      const [isPending, startTransition] = useTransition();
      const [value, setValue] = useState(0);
    
      function handleClick() {
        startTransition(() => {
          setValue((value) => value + 1);
        });
      }
    
      return (
        <div>
          {isPending && <Loader />}
          <button onClick={handleClick}>{value}</button>
        </div>
      );
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在 startTransition() 回调中提交的任何状态更新都将被标记为 transition,从而使其他更新具有优先权。如果你不能使用这个钩子,还有一个单独的 startTransition() 函数可用——虽然它不会通知你转换的进度。

    import { startTransition } from "react";
    // ...
    startTransition(() => {
      // Transition updates
    });
    // ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6、Suspense 更新

    与 React Suspense 结合使用时,transition 的效果是最好的。由于一些改进,Suspense 现在可以很好地与并发渲染集成、在服务器上工作,并且可能很快支持 lazy() 加载组件之外的用例。与 transition 一起使用时,Suspense 将避免隐藏现有内容。考虑以下示例:

    import { Suspense } from "react";
    // ...
    
    const App = () => {
      const [value, setValue] = useState("a");
      const handleClick = () => {
        setValue("b");
      };
    
      return (
        <>
          <Suspense fallback={<Loader />}>
            {value === "a" ? <A /> : <B />}
          </Suspense>
          <Button onClick={handleClick}>B</Button>
        </>
      );
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在状态改变时,lazy() 加载的组件将触发 Suspense,导致 fallback 元素的渲染。如果你将状态更改标记为一个 transition,React 将知道它应该在后台准备新视图,同时仍保持当前视图可见。

    import { Suspense, useTransition } from "react";
    // ...
    
    const App = () => {
      const [value, setValue] = useState("a");
      const [isPending, startTransition] = useTransition();
      const handleClick = () => {
        startTransition(() => {
          setValue("b");
        });
      };
    
      return (
        <>
          <Suspense fallback={<Loader />}>
            <div style={{ opacity: isPending ? 0.7 : 1 }}>
              {value === "a" ? <A /> : <B />}
            </div>
          </Suspense>
          <Button onClick={handleClick}>B</Button>
        </>
      );
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    现在,即使在处理 transition 时视图不会改变,你仍然可以使用过渡指示器来向用户提供反馈,例如设置容器不透明度。将上述改进与未来 Suspense 的新能力(与 lazy() 加载的组件之外的异步任务一起使用)相结合,意味着 Suspense 将成为 React 最强大的特性之一。

    7、服务端渲染改进

    除了 Suspense 支持之外,React 的 SSR 方面还有很多其他变化。将 Suspense 与 SSR 流式传输和懒惰水合(lazy hydration)相结合,意味着你的服务端渲染应用将尽快水合并可用。不仅如此,零打包体积的服务端组件即将到来。它们目前处于试验阶段,但可能会在以后的次要版本中进入稳定状态。使用它们时,你将能减少提供给客户端的 JS 代码,甚至进一步优化 React 应用程序的性能和加载时间。

    React 17 的多个更改,即使你的代码库很大,你也应该能够轻松地逐步采用 React 18。你不仅可以在应用程序的选定部分中使用新版本,还可以从 render() 迁移到 createRoot(),来一步步选择加入新的特性和行为。最重要的是,即使使用的是 createRoot(),你仍然可以逐步采用并发渲染,因为它只有在你使用它的特性时才会启用。总体而言,迁移过程应该很顺利。

    对 React 的展望

    React 18 带来了许多新特性,可以看到一些即将出现的新事物。服务器组件、用于数据获取的 Suspense,和组件渲染都是接下来的新特性的一部分。React 正在与它的整个生态系统一起发展,迫不及待地想看看接下来会发生什么。

    翻译:https://blog.openreplay.com/react-18-features-breakdown

  • 相关阅读:
    Ant Design Form.List基础用法
    java计算机毕业设计果蔬在线销售系统源码+mysql数据库+系统+LW文档+部署
    Spring框架中的事务处理
    【牛客 - 剑指offer】JZ11 旋转数组的最小数字 Java实现
    【动手学深度学习PyTorch版】10 PyTorch 神经网络基础
    柠檬水找零【贪心1】
    Linux下用C语言实现<<图书管理系统>>
    windows10家庭版如何开启组策略/地平线4Teredo服务器限定
    docker - 分享
    String、StringBuffer、StringBuilder的区别
  • 原文地址:https://blog.csdn.net/POHOU23/article/details/126035193