美文网首页
react18之startTransition

react18之startTransition

作者: 就问你怕不怕 | 来源:发表于2021-12-20 16:47 被阅读0次

    概述

    在React 18中,我们引入了一个新的API,帮助在应用程序即使在视图大量更新期间也能保持响应。这个新的API通过将特定的更新标记为“transition”,让您可以极大地提升用户交互体验。React将允许您在状态变更期间提供可视化反馈,并在状态变更发生时保持浏览器响应。

    它能解决什么样的问题?

    例如,考虑输入一个过滤数据列表的输入字段。您需要在状态中存储字段的值,以便您可以过滤数据并控制输入字段的值。你的代码可能看起来像这样: setSearchQuery(input);

    从概念上讲,问题在于需要进行两种不同的更新。

    • 第一个更新是紧急更新,去更改输入字段的值,可能还会更改它周围的一些UI;
    • 第二个显示搜索结果是不那么紧急的更新;
    • 每次输入时,第二次更新都会在之后触发

    第二次更新可能会有点延迟。用户并不需要它立即完成,这很好,因为它可能有很多工作要做。(事实上,开发人员经常使用比如“防抖”等技术人为地延迟这类更新。)
    在React18之前,所有更新都是紧急的。这意味着上述两种状态会同时更新,并且直到所有内容都呈现出来前会阻碍用户看到交互的反馈。我们需要一种方法,告诉React哪些是紧急,哪些不是紧急的更新。

    startTransition如何提供帮助?

    import { startTransition } from 'react';        // Class 组件和 Hooks 组件
    
    // 紧急的: 展示输入
    setInputValue(input);
    
    // 将内部的任何状态更新都标记为转换
    startTransition(() => {
      // Transition: 展示结果
      setSearchQuery(input);
    });
    
    

    在startTransition中包装的更新被作为非紧急的来处理,并且当更紧急的更新(如单击或按键)传入时将被中断。如果一个transition被用户打断(例如,在一行中键入多个字符),React会抛弃未完成的旧更新任务,并只渲染最新的更新。
    Transitions可以保持大多数的交互的快速更新,即使它们导致规模巨大的UI更改。它们还可以让您避免在渲染不相关的内容上浪费时间。

    什么是Transition?

    状态更新分为两类:

    • 紧急更新反映直接的交互,比如打字、单击、按下等等。
    • Transition更新将UI从一个视图转换到另一个视图。

    紧急更新,如键入、过滤下拉、单击或按下,需要立即响应,以符合我们对物理动作的直观感受。否则他们会觉得“出错了”。然而,使用transitions却得到另一种感受,因为用户不希望看到屏幕上的每一个中间状态呈现的效果。轻微的延迟是难以察觉的,而且往往是所期望的。如果在效果呈现之前再次更改过滤器,则只关心看到最新的效果。
    在典型的React应用中,大多数更新都是概念意义上的transition更新,(出于向后兼容性的原因) transitions是可选的。默认情况下,React 18仍然将更新处理为紧急的,并且您可以通过将更新包装到startTransition中将其标记为“transition”。

    它与setTimeout有什么不同?

    • SetTimeout被安排在稍后,而startTransition立即执行。传递给startTransition的函数是同步运行的,但是其中的任何更新都被标记为“transitions”。React将在稍后处理更新时使用这些信息来决定如何呈现更新。这意味着我们开始渲染更新的时间要比包装在timeout中的更新要早。在高性能设备上,两次更新之间的延迟非常小。在较慢的设备上,延迟会更大,但UI会保持响应。
    • 另一个重要的区别是setTimeout内部的大屏幕更新仍然会锁定页面,就在超时之后。但是标记为startTransition的状态更新是可中断的,所以它们不会锁定页面。它们允许浏览器在呈现不同组件之间的小间隙中处理事件。如果用户输入发生了变化,React就会继续呈现有关最新变化的内容。
    • 最后,由于编写异步代码,通常很容易显示setTimeout的加载指示器;但是使用转换api, React可以跟踪挂起状态,根据转换的当前状态更新它,并让你能够在用户等待时显示加载反馈。

    适合使用transition的场景?

    使用startTransition包装任何你想要移到背后的更新。通常,这些类型的更新分为两类:

    • 渲染缓慢: 这样的更新需要时间,因为React需要执行大量的工作来转换UI以显示结果。这是一个添加了startTransition的 real-world demo,以使得应用程序在一个高开销的重新渲染中间保持响应。
    • 网络慢: 这样的更新需要时间,因为React正在等待一些来自网络的数据。

    API

    import { startTransition } from 'react';        // Class 组件和 Hooks 组件
    
    // 紧急的: 展示输入
    setInputValue(input);
    
    // 将内部的任何状态更新都标记为转换
    startTransition(() => {
      // Transition: 展示结果
      setSearchQuery(input);
    });
    
    // hooks用法
    import { useTransition } from 'react';
    const [isPending, startTransition] = useTransition();
    

    参考:
    一个现实中的例子:
    https://gitlab.porsche-preview.cn/porsche-digital-china/web/opensource-projects/platform-app.git
    (这个仓库拉取下来后接口不通,无法展示,请见下面的简例:)

    点击进入:高开销示例

    import React, {  memo } from "react";
    import * as ReactDOM from "react-dom";
    /*  模拟数据  */
    const mockDataArray = new Array(3).fill(1);
    /* 高量显示内容 */
    function ShowText({ query }) {
      const text = "asdfghjk";
      let children;
      if (query !== "" && (text.indexOf(query) > 0 || text.indexOf(query) === 0)) {
        /* 找到匹配的关键词 */
        const arr = text.split(query);
        console.log(arr);
        children = (
          <div>
            {arr[0]}
            <span style={{ color: "pink" }}>{query}</span>
            {arr[1]}{" "}
          </div>
        );
      }
      return <div>{children}</div>;
    }
    /* 列表数据 */
    function List({ query }) {
      console.log("List渲染");
      return (
        <div>
          {mockDataArray.map((item, index) => (
            <div key={index}>
              <ShowText query={query} />
            </div>
          ))}
        </div>
      );
    }
    /* memo 做优化处理  */
    const NewList = memo(List);
    
    function App() {
      const [value, setInputValue] = React.useState("");
      const [isTransition, setTransion] = React.useState(false);
      const [query, setSearchQuery] = React.useState("");
      const handleChange = (e) => {
        /* 高优先级任务 —— 改变搜索条件 */
        setInputValue(e.target.value);
        if (isTransition) {
          /* transition 模式 */
          React.startTransition(() => {
            /* 低优先级任务 —— 改变搜索过滤后列表状态  */
            setSearchQuery(e.target.value);
          });
        } else {
          /* 不加优化,传统模式 */
          setSearchQuery(e.target.value);
        }
      };
      return (
        <div>
          <button onClick={() => setTransion(!isTransition)}>
            {isTransition ? "transition" : "normal"}{" "}
          </button>
          <input onChange={handleChange} placeholder="输入搜索内容" value={value} />
          <NewList query={query} />
        </div>
      );
    }
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(<App />);
    

    这个示例中,左侧按钮控制是否使用startTransition,可以很明显看到,未启用startTransition“低开销”输入卡顿,属于“高开销”的列表展示延迟明显。当启用startTransition“低开销”输入变得顺畅许多。
    另外,上文提到了debounce来解决高开销的问题,那么它与startTransition有什么区别呢?
    经过测试,debounce可以短效让输入变得顺畅

    其他参考:知乎

    相关文章

      网友评论

          本文标题:react18之startTransition

          本文链接:https://www.haomeiwen.com/subject/mowifrtx.html