一文搞懂 React 18 中的 useTransition() 与 useDeferredValue()

前言 React 18 引入了一个关键的新概念,称为“Concurrent”。 并发涉及同时执行多个状态更新,这可以说是 React 1

前言

React 18 引入了一个关键的新概念,称为“Concurrent”。

并发涉及同时执行多个状态更新,这可以说是 React 18 中最重要的特性。除了并发之外,React 18 还引入了两个新的钩子,称为 useTransition() 和 useDeferredValue() 钩子。

它们都有助于降低状态更新的优先级,但问题是,何时应该使用它们?

什么是Concurrent React?

根据官方 React18 Docs,并发 React 是:

一种新的幕后机制,使 React 能够同时准备多个版本的 UI。您可以将并发视为一个实现细节——它的价值在于它的特性。

并发可以定义为同时执行大量任务的能力。并发并不是一个特性。相反,它是一个幕后功能,它允许 React 同时(并发地)准备许多 UI 实例。 React 以一种对开发人员隐藏实现细节的方式创建 API。 React 创建者认为,React 开发人员应该专注于 React 功能将如何帮助他们实现他们希望为客户提供的用户体验,并且 React 将弄清楚如何提供这种体验。

Concurrent React 不仅仅是一个实现细节。相反,它是更新框架核心渲染架构的重大升级。因此,了解它在 React 18 中是如何工作的至关重要。

设置项目

对于本文,我创建了一个 Github 存储库,在其中我构建了一个简单的演示产品应用程序,允许用户通过输入产品编号来查看产品。首先,您必须克隆包含所有项目启动文件的 Github 存储库。要克隆 Github 存储库,请转到终端并运行以下命令:

git clone https://github.com/geekskai/react18-feature.git

完成克隆过程后,转到项目文件夹,在代码编辑器上打开它,转到编辑器终端,然后通过运行npm install 或安装脚本和依赖项 yarn install然后,要启动项目,请执行 npm start

应用程序启动后,它应该如下所示:

实现 useTransition()

当您第一次输入产品编号以获取product时,您会注意到它几乎会立即更新,即使它是一个包含 10,000 项要浏览的超长列表。

现在,当您转到 Chrome 浏览器的开发人员工具部分,转到性能选项卡,打开 CPU 节流并将 CPU 速度降低 4 倍时,问题就开始了。

如果您现在在降低 CPU 速度后尝试输入产品编号,您会注意到更新变得更慢且卡顿明显。甚至整个界面看起来和感觉都很迟钝,尤其是输入字段,现在在我们输入和删除时感觉没有响应。 而这绝对不是一个好的用户体验。

你看不到我在打字,但界面响应很慢,如上所示。甚至在 React18 之前,一个标准的解决方案不是一次处理 10,000 个项目或产品(在我们的例子中)。您可以使用分页或任何其他技术,或者在服务器端而不是客户端进行过滤。这些都是您在遇到此类问题时可以考虑的所有可能的解决方案。

但是,如果您需要在客户端执行这种操作,即在您的客户端代码上,那么使用 React18,您现在拥有一些工具,可以通过延迟一些状态更新操作来为用户提供更好的感知性能通过告诉 React 一些更新操作比其他操作具有更高的优先级。这就是 React 18 引入的并发以及相关的钩子和函数背后的想法。

useTransition() 告诉 React 一些状态更新具有较低的优先级,即每个其他状态更新或 UI 渲染触发器具有较高的优先级。当我们调用 useTransition() 时,我们得到一个包含两个元素的数组:一个 isPending 布尔值,它指示低优先级状态更新是否仍处于挂起状态,以及一个 startTransition() 函数,您可以将状态更新包装起来告诉 React这是一个低优先级的更新。

查看如何使用 useTransition() 钩子。首先,转到 App.js 文件并编辑代码,如下所示:

function App() {
  const [isPending, startTransition] = useTransition();
  const [filterTerm, setFilterTerm] = useState('');
  
  const filteredProducts = filterProducts(filterTerm);
  
  function updateFilterHandler(event) {
    startTransition(() => {
      setFilterTerm(event.target.value);
    });
  }
 
  return (
    <div id="app">
      <input type="text" onChange={updateFilterHandler} />
      <ProductList products={filteredProducts} />
    </div>
  );
}

因为 startTransition() 包装了 setFilterTerm() 状态更新函数,所以 React 给这个状态更新代码一个较低的优先级。这可确保输入字段保持响应并立即响应演示应用程序中的击键。如果未使用 useTransition(),应用程序可能会变得无响应,尤其是在速度较慢的设备上。当您输入产品编号时,您会看到代码现在响应速度更快且延迟更少,即使 CPU 已减慢 4 倍。您可以在您的系统上尝试此操作并查看结果。

但是,您不应该开始使用 startTransition 来结束所有状态更新。仅当您的用户界面较慢时才使用它,尤其是在旧设备上,或者在您没有其他解决方案可使用的情况下。这是因为它占用了额外的性能。

isPending 是做什么的?

isPending 告诉您当前是否有一些状态更新仍处于待处理状态(React 尚未执行,并且优先级较低。您可以使用 isPending 更新 UI 以在等待主要状态时显示一些后备内容更新完成。

我们可以在 App.ts 文件中的以下代码中看到这一点:

return (
    <div id="app">
      <input type="text" onChange={updateFilterHandler} />
      {isPending && <p style={{color: 'white'}}>Updating list..</p>}
      <ProductList products={filteredProducts} />
    </div>
  );
}

实现代码后,您应该能够在运行应用程序时看到如下内容:

因此,这也是您在使用 useTransition() 时可以使用的功能。另外,请注意观察实现 useTransition() 功能后它的响应速度有多快。

到此这篇关于一文搞懂 React 18 中的 useTransition() 与 useDeferredValue()的文章就介绍到这了,更多相关React useDeferredValue内容请搜索好代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持好代码网!