概述
在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
可以短效让输入变得顺畅
其他参考:知乎