我正在测试一个阻止我的 UI 渲染的繁重计算,并使用useTransition
钩子来修复它并在更新时显示加载。
应用程序.tsx
import { useEffect, useState, useTransition } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const [isPending, startTransition] = useTransition();
const handleClick = () => {
console.log("Starting Transition...");
startTransition(() => {
for (let i = 0; i < 1000000000; i++) {}
setCount((prevCount) => prevCount + 1);
console.log("Ending Transition...");
});
};
useEffect(() => {
console.log(isPending);
}, [isPending]);
return (
<div className="App">
<div>
<h1>{count}</h1>
<button disabled={isPending} onClick={handleClick}>
{isPending ? "Loading..." : "Increment"}
</button>
</div>
{isPending && "Loading"}
</div>
);
}
问题是,isPending
在完成后不久就变为真useTransition
,当我单击按钮时,日志如下:
日志
Starting Transition...
Ending Transition...
true
false
我的问题是,为什么isPending
在转换开始时转变不成立,而在转换结束后转变不成立?
startTransition
调用它时不会导致同步重新渲染。同步重新渲染是存在的,但它们可能会带来性能问题,而转换旨在提高可感知的性能,而不是损害性能。相反,当你调用时,
startTransition
你告诉 React“我在此函数内设置状态的任何调用都是低优先级的”。React 会注意到这一点,然后调用你的函数,然后你可以根据需要多次设置状态。一旦你的函数返回,React 会排队一个正常优先级的重新渲染以设置isPending
为 true。之后,它会执行低优先级渲染以应用你所做的状态更改。因此,
startTransition
它不是为你设计用来做大量同步工作。它对此毫无帮助。你可以startTransition
在完成一些繁重的工作后调用它。但这很有用,不是因为它会破坏你的同步代码,而是因为生成的渲染优先级较低。如果渲染本身可能需要一段时间,或者你预计用户会执行高优先级的事情(例如,在输入字段中键入内容),而你不想减慢速度,那么低优先级渲染就很有用。如果您想要拆分一些同步代码,则必须编写自定义代码来实现这一点。例如,您可以拥有一系列
setTimeout
s,每个 s 运行一段代码,然后排队等待下一个 setTimeout,从而让浏览器有机会运行其他代码。