如何解决React子组件中的逻辑很多影响父组件回显速度的问题

前言

更新状态导致重新渲染时,由于子组件中的逻辑很多,影响到父组件的回显速度。

React18之前,由于渲染是同步的,一旦开始渲染,就不可被中断,所谓的同步,就是指如果react的某个组件执行时间长,它无法中断,会一直执行,直到组件完全渲染到DOM中。在这个过程中,由于Javascript是单线程的,因此渲染任务会占满JavaScript线程,阻塞浏览器的主线程,从而导致用户无法进行交互操作。

但React18之后引入了并发模式,并发指的就是通过time slice将任务拆分为多个,然后react根据优先级来完成调度策略,将低优先级的任务先挂起,将高优先级的任务分配到浏览器主线程的一帧的空闲时间中去执行,如果浏览器在当前一帧中还有剩余的空闲时间,那么React就会利用空闲时间来执行剩下的低优先级的任务。react的渲染和更新可以被中断和恢复。那么如果在执行某个组件更新过程中又有了新的更新请求到达。比如我们下面的input输入事件,那么React就会创建一个新的更新版本。这种情况下,在某个时间段内可能会同时存在多个更新版本

为了优化上述问题,React 18 提供了新的 Hook 函数 useTransition,它可以将多个版本的更新打包到一起,在未来的某一帧空闲时间内执行,从而优化应用的性能和响应时间。而useDeferredValue 的作用是将某个值的更新推迟到未来的某个时间片内执行,从而避免不必要的重复渲染和性能开销。

解决方法一

useTransition

使用startTransition将逻辑很多的组件变为过渡任务,使其不会影响父组件的回显速度。

可以直接引入startTransition或者使用useTransition,useTransition返回一个等待状态(过渡任务是否已经执行成功)以及一个启动该过渡任务的函数(与直接引入startTransition一样)

示例

依赖部分

可以直接引入startTransition,如果想获取任务执行状态,需使用useTransition

// 可以直接引入startTransition,如果想获取任务执行状态,需使用useTransition
import { useState, startTransition, useEffect, useTransition } from "react"
import { Input } from "antd"

子组件(模拟逻辑复杂处理缓慢的组件)

// 接收父组件中Input输入的值
const Son = ({ query }) => {const content = []const str = "hello world"const handler = () => {// 将str中包含query的部分变为粉色if (query && str.includes(query)) {const arr = str.split(query)// 模拟很耗时的操作for (let i = 0; i < 3000; i++) {content.push(<li key={i}><span>{arr[0]}</span><span style={{ color: "pink" }}>{query}</span><span>{arr[1]}</span></li>)}} else {for (let i = 0; i < 3000; i++) {content.push(<li key={i}>{str}</li>)}}return content}return <ul>{handler()}</ul>
}

父组件

如果不将第二个set函数变为过渡任务,每次父组件中Input的值会等待子组件处理完毕后才进行回显。

const Father = () => {const [inputValue, setInputValue] = useState("")const [query, setQuery] = useState("")// pending(布尔值),延迟执行未成功前为false成功后变为trueconst [pending, startTransition] = useTransition()const onInputChange = (e) => {// 默认全是紧急任务setInputValue(e.target.value)// 如果此处也为紧急任务,会等待页面渲染完毕后再回显// setQuery(e.target.value)  // 被startTransiton处理过后,此时变为了非紧急任务,并不会影响Input中值的回显速度startTransition(() => setQuery(e.target.value))}return (<><Input value={inputValue} onChange={onInputChange} />{/* 可以根据pending(过渡任务执行状态),进行相应提示 */}{pending && <span>等待中...</span>}<Son query={query} /></>)
}

整体代码

// 可以直接引入startTransition,如果想获取任务执行状态,需使用useTransition
import { useState, startTransition, useEffect, useTransition } from "react"
import { Input } from "antd"// 子组件模拟很耗时的操作
const Son = ({ query }) => {const content = []const str = "hello world"const handler = () => {// str中包含query的部分变为粉色if (query && str.includes(query)) {const arr = str.split(query)for (let i = 0; i < 3000; i++) {content.push(<li key={i}><span>{arr[0]}</span><span style={{ color: "pink" }}>{query}</span><span>{arr[1]}</span></li>)}} else {for (let i = 0; i < 3000; i++) {content.push(<li key={i}>{str}</li>)}}return content}return <ul>{handler()}</ul>
}const Father = () => {const [inputValue, setInputValue] = useState("")const [query, setQuery] = useState("")// pending(布尔值),延迟执行未成功前为false成功后变为trueconst [pending, startTransition] = useTransition()const onInputChange = (e) => {// 默认全是紧急任务setInputValue(e.target.value)// 如果此处也为紧急任务,会等待页面渲染完毕后再回显// setQuery(e.target.value)  // 被startTransiton处理过后,此时变为了非紧急任务,并不会影响Input中值的回显速度startTransition(() => setQuery(e.target.value))}return (<><Input value={inputValue} onChange={onInputChange} />{pending && <span>等待中...</span>}<Son query={query} /></>)
}export default Father

解决方法二

useDeferredValue

useDeferredValue接受一个值,并返回该值的新副本,该副本将推迟到更紧急的更新之后。

实例

依赖

import { useState, useDeferredValue } from "react"
import { Input } from "antd"

子组件(同上,子组件中无需处理)

// 接收父组件中Input输入的值
const Son = ({ query }) => {const content = []const str = "hello world"const handler = () => {// 将str中包含query的部分变为粉色if (query && str.includes(query)) {const arr = str.split(query)// 模拟很耗时的操作for (let i = 0; i < 3000; i++) {content.push(<li key={i}><span>{arr[0]}</span><span style={{ color: "pink" }}>{query}</span><span>{arr[1]}</span></li>)}} else {for (let i = 0; i < 3000; i++) {content.push(<li key={i}>{str}</li>)}}return content}return <ul>{handler()}</ul>
}

父组件

useDeferredValue(inputValue),得到一个延迟的副本,值和inpuValue一样。

const UseDeferredValue = () => {const [inputValue, setInputValue] = useState("")const query = useDeferredValue(inputValue)const onInputChange = (e) => {// 默认全是紧急任务,需等待所有set完成后,再进行渲染setInputValue(e.target.value)}return (<><Input value={inputValue} onChange={onInputChange} /><Son query={query} /></>)
}

整体代码

import { useState, useDeferredValue } from "react"
import { Input } from "antd"const Son = ({ query }) => {const content = []const str = "hello world"const handler = () => {if (query && str.includes(query)) {const arr = str.split(query)for (let i = 0; i < 3000; i++) {content.push(<li key={i}><span>{arr[0]}</span><span style={{ color: "pink" }}>{query}</span><span>{arr[1]}</span></li>)}} else {for (let i = 0; i < 3000; i++) {content.push(<li key={i}>{str}</li>)}}return content}return <ul>{handler()}</ul>
}const UseDeferredValue = () => {const [inputValue, setInputValue] = useState("")const query = useDeferredValue(inputValue)const onInputChange = (e) => {// 默认全是紧急任务,需等待所有set完成后,再进行渲染setInputValue(e.target.value)}return (<><Input value={inputValue} onChange={onInputChange} /><Son query={query} /></>)
}export default UseDeferredValue

两种方法的区别

useDeferredValue的作用和useTransition一致,都是用于在不阻塞UI的情况下更新状态。但是使用场景不同。

useTransition是让你能够完全控制哪个更新操作应该以一个比较低的优先级被调度。但是,在某些情况下,可能无法访问实际的更新操作(例如,状态是从父组件上传下来的)。这时候,就可以使用useDeferredValue来代替。

useTransition直接控制更新状态的代码,而useDeferredValue控制一个受状态变化影响的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/170311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatisPlus入门介绍

目录 一、MyBatisPlus介绍 润物无声 效率至上 丰富功能 二、Spring集成MyBatisPlus 三、SpringBoot集成MyBatisPlus 一、MyBatisPlus介绍 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c…

Kafka系列 - Kafka一篇入门

Kafka是一个分布式流式处理平台。很多分布式处理系统&#xff0c;例如Spark&#xff0c;Flink等都支持与Kafka集成。 Kafka使用场景 消息系统&#xff1a;Kafka实现了消息顺序性保证和回溯消费。存储系统&#xff1a;Kafka把消息持久化到磁盘&#xff0c;相比于其他基于内存的…

如何处理git多分支

本篇文章主要处理以下两种多分支问题 如何将自己在本地的修改上传到一个新的Git分支&#xff08;比如用于测试&#xff0c;不合并进main分支&#xff09;&#xff1f;如何在一个新的本地仓库拉取一个项目的非main分支&#xff0c;并处理他们关联关系&#xff1f; 1. 将自己在…

java基于springboot公益帮学网站 新闻发布系统的设计与实现vue

以Java为开发平台&#xff0c;综合利用Java Web开发技术、数据库技术等&#xff0c;开发出公益帮学网站。用户使用版块&#xff1a;可以选择注册并登录&#xff0c;可以浏览信息、可以网上互动、发布文章、内容推荐等。后台管理员管理版块&#xff1a;以管理员身份登录网站后台…

C# 读写FDX-B(ISO11784/85)动物标签源码

本示例使用的发卡器&#xff1a;EM4305 EM4469 ISO11784/85协议125K低频FXD-B动物标签读写发卡器-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using S…

rk3588配置uac功能,android13使能uac及adb的复合设备

最近&#xff0c;因新增需求需要在现有产品上增加UAC的功能&#xff0c;查阅并学习相关知识后&#xff0c;在rk3588 SOC硬件平台搭载android13系统平台上成功配置了uac及uac&adb的复合设备。基于开源共享精神希望给大家提供些参考。 1.技术可行性预研 &#xff08;1&#…

【一起来学kubernetes】7、k8s中的ingress详解

引言配置示例负载均衡的实现负载均衡策略实现模式实现方案Nginx类型Ingress实现Treafik类型Ingress实现HAProxy类型ingress实现Istio类型ingress实现APISIX类型ingress实现 更多 引言 Ingress是Kubernetes集群中的一种资源类型&#xff0c;用于实现用域名的方式访问Kubernetes…

人工智能-注意力机制之残差连接和层规范化

残差连接和层规范化 层规范化和批量规范化的目标相同&#xff0c;但层规范化是基于特征维度进行规范化。尽管批量规范化在计算机视觉中被广泛应用&#xff0c;但在自然语言处理任务中&#xff08;输入通常是变长序列&#xff09;批量规范化通常不如层规范化的效果好。 以下代…

自建私有化证书颁发机构(Certificate Authority,CA)实战之 《0x02 Nginx 配置 https双向认证》

自建CA实战之 《0x02 Nginx 配置 https双向认证》 上一章节我们已经实现了Nginx上配置https单向认证&#xff0c;主要场景为客户端验证服务端的身份&#xff0c;但是服务端不验证客户端的身份。 本章节我们将实现Nginx上配置https双向认证&#xff0c;主要场景为客户端验证服…

C++ 实现位图

引出 面试题&#xff1a;给出 40 亿个不重复的无符号整数&#xff0c;没有排过序。给定一个无符号整数&#xff0c;如何快速判断这个数是否在这 40 亿个无符号整数中。[ 腾讯面试题 ] 想法一&#xff1a;将 40 亿个数据存放到 set 里面&#xff0c;然后再查找指定的无符号整数。…

Python基础入门例程75-NP75 使用字典计数(字典)

最近的博文: Python基础入门例程74-NP74 字典新增(字典)-CSDN博客 Python基础入门例程73-NP73 查字典(字典)-CSDN博客 Python基础入门例程72-NP72 生成字典(字典)-CSDN博客 目录 最近的博文:

论文阅读——MCAN(cvpr2019)

补充一下MCAN-VQA&#xff1a; 对图片的处理&#xff1a;首先输入图片到Faster R-CNN&#xff0c;会先设定一个判断是否检测到物体的阈值&#xff0c;这样动态的生成m∈[10,100]个目标&#xff0c;然后从检测到的对应的区域通过平均池化提取特征。第i个物体特征表示为&#xff…

MUYUCMS v2.1:一款开源、轻量级的内容管理系统

MuYuCMS&#xff1a;一款基于Thinkphp开发的轻量级开源内容管理系统&#xff0c;为企业、个人站长提供快速建站解决方案。它具有以下的环境要求&#xff1a; 支持系统&#xff1a;Windows/Linux/Mac WEB服务器&#xff1a;Apache/Nginx/ISS PHP版本&#xff1a;php > 5.6 (…

3.3.4打开cpu初始化大门之cpumask

文章目录 作用初始化定义接口参考作用 初始化 // in kernel/cpu.c void __init boot_cpu_init(void) {int cpu = smp_processor_id();set_cpu_online(cpu, true);set_cpu_active(cpu, true);set_cpu_present(cpu, true);set_cpu_possible(cpu, true); ​ #ifdef CONFIG_SMP__…

发布鸿蒙的第一个java应用

1.下载和安装华为自己的app开发软件DevEco Studio HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 2.打开IDE新建工程&#xff08;当前用的IDEA 3.1.1 Release&#xff09; 选择第一个&#xff0c;其他的默认只能用(API9)版本&#xff0c;搞了半天才发现8&#xff…

11 月 25 日 ROS 学习笔记——3D 建模与仿真

文章目录 前言一、在 ROS 中自定义机器人的3D模型1. 在 rviz 里查看3D模型2. xacro 二、Gazebo1. urdf 集成 gazebo2. 综合应用1). 运动控制及里程计2). 雷达仿真3). 摄像头信息仿真4). kinect 深度相机仿真5). 点云 前言 本文为11 月 25 日 ROS 学习笔记——3D 建模与仿真&am…

BART non-Cartesian 重建:并行成像 压缩感知

本文主要使用并行成像和压缩感知方法实现non-Cartesian MRI 数据的重建。 目录 1 自定义MRI kspace trajectory 2 自定义该 trajectory下的多通道MRI数据 3 使用NUFFT 直接做欠采样数据的重建

使用UIActivityViewController分享图片,没有preview

以前都是用第三方sdk来分享的&#xff0c;最近使用官方的UIActivityViewController来做分享&#xff0c;结果分享图片的时候preview不了分享的图片。 自定义一个继承UIActivityItemProvider的类。关于分享的内容自定义可以自己实现UIActivityItemSource这个协议。首先看看协议的…

STK Components 基础篇

1.开发包 STK Components 访问AGI官网&#xff0c;注册并登录后&#xff0c;从官网下载开发包&#xff1a;https://support.agi.com/downloads/&#xff0c;下载成功后可以申请许可证&#xff0c;AGI会向你注册的邮箱地址发送有效期半年的使用授权许可文件&#xff08;lic文件…

【LeeCode】26.删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums &#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你需…