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

目录
  • 前言
  • 什么是Concurrent React?
  • 设置项目
  • 实现 useTransition()
  • isPending 是做什么的?

前言

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React18中的useDeferredValue示例详解

    目录 有什么用? 实操起来 跟防抖的区别 接着上一篇React18的简介和自动批处理的特性,今天我们来聊下useDeferredValue. 有什么用? lets you defer updating the less important parts of the screen 我们翻译下,允许用户推迟屏幕更新优先级不高部分. 说“人话”就是,如果说某些渲染比较消耗性能,比如存在实时计算和反馈,我们可以使用这个Hook降低其计算的优先级,使得避免整个应用变得卡顿. 较多的场景可能就在于用户反馈输

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

    目录 前言 什么是Concurrent React? 设置项目 实现 useTransition() isPending 是做什么的? 前言 React 18 引入了一个关键的新概念,称为“Concurrent”. 并发涉及同时执行多个状态更新,这可以说是 React 18 中最重要的特性.除了并发之外,React 18 还引入了两个新的钩子,称为 useTransition() 和 useDeferredValue() 钩子. 它们都有助于降低状态更新的优先级,但问题是,何时应该使用它们? 什

  • 一文搞懂Spring Bean中的作用域和生命周期

    目录 一.Spring Bean 作用域 singleton(单例) prototype(原型) 小结 二.Spring Bean生命周期 如何关闭容器 生命周期回调 通过接口设置生命周期 通过xml设置生命周期 一.Spring Bean 作用域 常规的 Spring IoC 容器中Bean的作用域有两种:singleton(单例)和prototype(非单例) 注:基于Web的容器还有其他种作用域,在这就不赘述了. singleton(单例) singleton是Spring默认的作用域.当

  • 一文搞懂Vue3.2中setup语法糖使用

    目录 前言 一.如何使用setup语法糖 二.data数据的使用 三.method方法的使用 四.watchEffect的使用 五.watch的使用 六.computed计算属性的使用 七.props父子传值的使用 八.emit子父传值的使用 九.获取子组件ref变量和defineExpose暴露 十.路由useRoute和useRouter的使用 十一.store仓库的使用 十二.await 的支持 十三.provide 和 inject 祖孙传值 前言 提示:Vue3.2 版本开始才能使用语

  • 一文搞懂JMeter engine中HashTree的配置问题

    目录 一.前言 二.HashTree的用法 三.JMeter源码导出jmx脚本文件介绍 四.自定义HashTree生成JMeter脚本 一.前言 之前介绍了JMeter engine启动原理,但是里面涉及到HashTree这个类结构没有给大家详细介绍,这边文章就详细介绍JMeter engine里面的HashTree结构具体用来做什么 大家看到下面是JMeter控制台配置截图,是一个标准的菜单形式:菜单形式其实就类似于"树型"的数据结构,而HashTree其实就是一个树型数据结构 我们

  • 一文搞懂Go语言中条件语句的使用

    目录 if语句 if...else 语句 if 语句嵌套 switch 语句 Type Switch fallthrough select 语句 条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句. Go 语言提供了以下几种条件判断语句: 语句 描述 if 语句 if 语句 由一个布尔表达式后紧跟一个或多个语句组成. if...else 语句 if 语句 后可以使用可选的 else 语句, else 语

  • 一文搞懂Go语言中文件的读写与创建

    目录 1. 文件的打开与关闭 1.1 os.open 1.2 os.OpenFile() 指定模式打开文件 2. 文件的读取 2.1 打开文件的方式读取文件中的数据 2.2 使用 bufio 整行读取文件 3. 写入文件操作 3.1 file.Write 与 file.WriteString 3.2 bufio.NewWriter 3.3 ioUtil 工具类 1. 文件的打开与关闭 1.1 os.open os.open 函数能打开一个文件 调用 close() 方法 关闭文件 //打开文件

  • 一文搞懂Java项目中枚举的定义与使用

    目录 什么是枚举 为什么需要枚举类 枚举类的定义和使用 什么是枚举 最近写新项目!有很多数据字典常量需要定义和使用.就顺便记录一下.什么是枚举类呢?就是用enum修饰是一种Java特殊的类,枚举是class.底层是继承了java.lang.Enum类的实体类.使用枚举可以很方便的定义数据常量.方便清晰我们使用 为什么需要枚举类 下面就举例说明一下吧 1)出于类型安全考虑,没用枚举类之前,常用静态常量来表示. 比如对于性别的表示: public static final int WOMAN = 0

  • 一文搞懂Java JDBC中的SQL注入问题

    目录 SQL注入 什么是SQL注入 SQL注入的效果的演示 SQL注入代码 SQL注入效果 如何避免SQL注入 PrepareStatement解决SQL注入 PreparedStatement的应用 参数标记 动态参数绑定 综合案例 PreparedStatement总结 必须使用Statement的情况 SQL注入 什么是SQL注入 在用户输入的数据中有SQL关键字或语法,并且关键字或语法参与了SQL语句的编译.导致SQL语句编译后的条件为true,一直得到正确的结果.这种现象就是SQL注入

  • 教你一文搞懂Kotlin中的Jvm注解

    JvmOverloads 创建一个kotlin的类 class Student(val name: String, val sex: Int = 1, val age: Int = 18) 可以看出来 这个构造函数的参数是有默认值的,kotlin的特性对吧,我们在使用的时候可以方便的使用,比如: val student = Student("wuyue") val student2 = Student("wuyue", age = 18) 但是这个特性如果你用jav

  • 一文搞懂JavaScript中原型与原型链

    目录 1.构造函数原型prototype 2.对象原型__proto__ 3.constructor构造函数 4.原型链 5.原型对象中的this指向 6.扩展内置对象(原型对象的应用) 在ES6之前,我们面向对象是通过构造函数实现的.我们把对象的公共属性和方法放在构造函数里 像这样: function student(uname,age) { this.uname = uname; this.age = age; this.school = function() { console.log('

随机推荐