react18之startTransition

2021-12-20  本文已影响0人  就问你怕不怕

概述

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

它能解决什么样的问题?

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

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

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

startTransition如何提供帮助?

import { startTransition } from 'react';        // Class 组件和 Hooks 组件

// 紧急的: 展示输入
setInputValue(input);

// 将内部的任何状态更新都标记为转换
startTransition(() => {
  // Transition: 展示结果
  setSearchQuery(input);
});

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

什么是Transition?

状态更新分为两类:

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

它与setTimeout有什么不同?

适合使用transition的场景?

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

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可以短效让输入变得顺畅

其他参考:知乎

上一篇下一篇

猜你喜欢

热点阅读