React-Hooks怎样封装防抖和节流-面试真题
Debounce
debounce 原意
消除抖动
,对于事件触发频繁的场景,只有最后由程序控制的事件是有效的。
防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器,直到定时器到时仍不被清除,事件才真正发生。
const debounce = (fun, delay) => {let timer;return (...params) => {if (timer) {clearTimeout(timer);}timer = setTimeout(() => {fun(...params);}, delay);};
};
如果事件发生使一个变量频繁变化,那么使用debounce
可以降低修改次数。通过传入修改函数,获得一个新的修改函数来使用。
如果是class
组件,新函数可以挂载到组件this
上,但是函数式组件局部变量每次render
都会创建,debounce
失去作用,这时需要通过useRef
来保存成员函数(下文throttle
通过useRef
保存函数),是不够便捷的,就有了将debounce
做成一个hook
的必要。
function useDebounceHook(value, delay) {const [debounceValue, setDebounceValue] = useState(value);useEffect(() => {let timer = setTimeout(() => setDebounceValue(value), delay);return () => clearTimeout(timer);}, [value, delay]);return debounceValue;
}
在函数式组件中,可以将目标变量通过useDebounceHook
转化一次,只有在满足delay
的延迟之后,才会触发,在delay
期间的触发都会重置计时。
配合useEffect
,在debounce value
改变之后才会做出一些动作。下面的text
这个state
频繁变化,但是依赖的是debounceText
,所以引发的useEffect
回调函数却是在指定延迟之后才会触发。
const [text,setText]=useState('');
const debounceText = useDebounceHook(text, 2000);
useEffect(() => {// ...console.info("change", debounceText);
}, [debounceText]);function onChange(evt){setText(evt.target.value)
}
上面一个搜索框,输入完成1
秒(指定延迟)后才触发搜索请求,已经达到了防抖的目的。
Throttle
throttle 原意
节流阀
,对于事件频繁触发的场景,采用的另一种降频策略,一个时间段内只能触发一次。
节流函数相对于防抖函数用在事件触发更为频繁的场景上,滑动事件,滚动事件,动画上。
看一下一个常规的节流函数 (ES6):
function throttleES6(fn, duration) {let flag = true;let funtimer;return function () {if (flag) {flag = false;setTimeout(() => {flag = true;}, duration);fn(...arguments);// fn.call(this, ...arguments);// fn.apply(this, arguments); // 运行时这里的 this 为 App组件,函数在 App Component 中运行} else {clearTimeout(funtimer);funtimer = setTimeout(() => {fn.apply(this, arguments);}, duration);}};
}
参考 前端进阶面试题详细解答
(使用...arguments
和 call 方法调用展开参数及apply 传入argument的效果是一样的)
扩展:在
ES6
之前,没有箭头函数,需要手动保留闭包函数中的this
和参数再传入定时器中的函数调用:所以,常见的
ES5
版本的节流函数:function throttleES5(fn, duration) {let flag = true;let funtimer;return function () {let context = this,args = arguments;if (flag) {flag = false;setTimeout(function () {flag = true;}, duration);fn.apply(context, args); // 暂存上一级函数的 this 和 arguments} else {clearTimeout(funtimer);funtimer = setTimeout(function () {fn.apply(context, args);}, duration);}}; }
如何将节流函数也做成一个自定义Hooks
呢?上面的防抖的Hook
其实是对一个变量进行防抖的,从一个不间断频繁变化的变量
得到一个按照规则(停止变化delay时间后)
才能变化的变量。我们对一个变量的变化进行节流控制,也就是从一个不间断频繁变化的变量
到指定duration期间只能变化一次(结束后也会变化)
的变量。
throttle
对应的Hook
实现:
(标志能否调用值变化的函数的flag
变量在常规函数中通过闭包环境来保存,在Hook
中通过useRef
保存)
function useThrottleValue(value, duration) {const [throttleValue, setThrottleValue] = useState(value);let Local = useRef({ flag: true }).current;useEffect(() => {let timer;if (Local.flag) {Local.flag = false;setThrottleValue(value);setTimeout(() => (Local.flag = true), duration);} else {timer = setTimeout(() => setThrottleValue(value), duration);}return () => clearTimeout(timer);}, [value, duration, Local]);return throttleValue;
}
对应的在手势滑动中的使用:
export default function App() {const [yvalue, setYValue] = useState(0);const throttleValue = useThrottleValue(yvalue, 1000);useEffect(() => {console.info("change", throttleValue);}, [throttleValue]);function onMoving(event, tag) {const touchY = event.touches[0].pageY;setYValue(touchY);}return (<divonTouchMove={onMoving}style={{ width: 200, height: 200, backgroundColor: "#a00" }} />);
}
这样以来,手势的yvalue
值一直变化,但是因为使用的是throttleValue
,引发的useEffect
回调函数已经符合规则被节流,每秒只能执行一次,停止变化一秒后最后执行一次。
对值还是对函数控制
上面的Hooks
封装其实对值进行控制的,第一个防抖的例子中,输入的text
跟随输入的内容不断的更新state
,但是因为useEffect
是依赖的防抖之后的值,这个useEffect
的执行是符合防抖之后的规则的。
可以将这个防抖规则提前吗? 提前到更新state
就是符合防抖规则的,也就是只有指定延迟之后才能将新的value
进行setState
,当然是可行的。但是这里搜索框的例子并不好,对值变化之后发起的请求可以进行节流,但是因为搜索框需要实时呈现输入的内容,就需要实时的text
值。
对手势触摸,滑动进行节流的例子就比较好了,可以通过设置duration
来控制频率,给手势值的setState
降频,每秒只能setState
一次:
export default function App() {const [yvalue, setYValue] = useState(0);const Local = useRef({ newMoving: throttleFun(setYValue, 1000) }).current;useEffect(() => {console.info("change", yvalue);}, [yvalue]);function onMoving(event, tag) {const touchY = event.touches[0].pageY;Local.newMoving(touchY);}return (<divonTouchMove={onMoving}style={{ width: 200, height: 200, backgroundColor: "#a00" }} />);
}//常规节流函数
function throttleFun(fn, duration) {let flag = true;let funtimer;return function () {if (flag) {flag = false;setTimeout(() => (flag = true), duration);fn(...arguments);} else {clearTimeout(funtimer);funtimer = setTimeout(() => fn.apply(this, arguments), duration);}};
}
这里就是对函数进行控制了,控制函数setYValue
的频率,将setYValue
函数传入节流函数,得到一个新函数,手势事件中使用新函数,那么setYValue
的调用就符合了节流规则。如果这里依然是对手势值节流的话,其实会有很多的不必要的setYValue
执行,这里对setYValue
函数进行节流控制显然更好。
需要注意的是,得到的新函数需要通过
useRef
作为“实例变量”暂存,否则会因为函数组件每次render
执行重新创建。
相关文章:
React-Hooks怎样封装防抖和节流-面试真题
Debounce debounce 原意消除抖动,对于事件触发频繁的场景,只有最后由程序控制的事件是有效的。 防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器ÿ…...

算法训练营 day51 动态规划 打家劫舍系列
算法训练营 day51 动态规划 打家劫舍系列 打家劫舍 198. 打家劫舍 - 力扣(LeetCode) 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#…...

【蓝桥集训】第六天——递归
作者:指针不指南吗 专栏:Acwing 蓝桥集训每日一题 🐾或许会很慢,但是不可以停下来🐾 文章目录1.树的遍历2.递归求阶乘3.求斐波那契数列1.树的遍历 一个二叉树,树中每个节点的权值互不相同。 现在给出它的后…...

react源码中的hooks
今天,让我们一起深入探究 React Hook 的实现方法,以便更好的理解它。但是,它的各种神奇特性的不足是,一旦出现问题,调试非常困难,这是由于它的背后是由复杂的堆栈追踪(stack trace)支…...
038.Solidity入门——25调用其他合约的方法
Solidity 提供了几种方式用于调用其他合约:方法描述直接调用使用 address.call 函数,可以向另一个合约发送消息并返回结果。低级调用使用 address.call 或 address.callcode 函数,可以执行一个外部合约中的代码。与直接调用不同,低…...

Revit项目浏览器的标准设置应用和快速视图样板?
一、Revit项目浏览器的标准设置应用 设计院阶段的BIM应用,主要是Revit出施工图方面,需要涉及到很多标准的制定方面的问题,而且这个标准不仅仅是一个命名标准,还有很多的符合本院的出图标准等等,本期就不做详细讨论&…...

安装MQTT Server遇到报错“cannot verify mosquitto.org‘s certificate”,该如何解决?
MQTT是基于发布/订阅的轻量级即时通讯协议,很适合用于低带宽、不稳定的网络中进行远程传感器和控制设备通讯等操作中。在我们的软件研发中,也经常使用MQTT协议进行消息通信等。今天来和大家分享一些关于在安装MQTT Server中遇到的疑难问题及解决思路。当…...

程序员如何向架构师转型?看完就明白该怎么做了
软件行业技术开发从业人员众多,但具备若干年开发经验的普通的开发人员往往面临个人发展的瓶颈,即如何从普通开发人员转型成高层次的系统架构师和技术管理人员。想成为一名架构师,应当具备全面的知识体系,需要进行系统的学习和实践…...

Flask入门(9):蓝图
目录9.蓝图9.1 概述9.2 蓝图项目结构结构1结构29.3 添加前缀9.4 静态文件9.5 模板9.6 构建 URLs9.蓝图 参考:http://www.pythondoc.com/flask/blueprints.html 9.1 概述 Flask 使用了 蓝图 的概念在一个应用或者跨应用中构建应用组件以及支持通用模式。 蓝图很好…...

跑步戴哪种耳机好,最适合运动跑步的蓝牙耳机
经常跑步使用的耳机,还是要选择佩戴着舒适以及牢固的运动耳机最为合适,在运动当中会遇到耳机掉落或者长时间佩戴耳道感到难受的现象发生,那么什么蓝牙耳机是最适合运动当中佩戴呢?下面这些耳机分享希望能够帮助大家。 1、南卡Run…...
微信小程序实现瀑布流布局
微信小程序实现瀑布流布局1、简单实例,纯图片后台返回图片高度https://blog.csdn.net/qq_45967222/article/details/1190318762、纯图片后台返回图片高度、通过wx.getImageInfo获取在线图片高度、按照奇数偶数来显示https://blog.csdn.net/baidu_35290582/article/d…...

2023最新网络工程师HCIA-Datacom“1000”道题库,光速刷题拿证
HCIA认证是华为认证体系的初级认证,可以说是网工进入IT行业的一张从业资格证! HCIA-Datacom考试覆盖数通基础知识 包括 TCP/IP 协议栈基础知识,OSPF 路由协议基本原理以及在华为路由器中的配置实现,以太网技术、生成树、VLAN 原…...

[蓝桥杯] 递归与递推习题训练
文章目录 一、递归实现指数型枚举 1、1 题目描述 1、2 题解关键思路与解答 二、递归实现排列型枚举 2、1 题目描述 2、2 题解关键思路与解答 三、递归实现组合型枚举 3、1 题目描述 3、2 题解关键思路与解答 四、带分数 4、1 题目描述 4、2 题解关键思路与解答 五、费解的开关…...

领航智能汽车信息安全新征程 | 云驰未来乔迁新址
2月20日,在北京朝阳百子湾东朝时代创意园,云驰未来迎来乔迁之喜,智能汽车和自动驾驶领域的行业领导、合作伙伴与客户、投资人及媒体嘉宾齐聚现场,共同见证云驰未来迈上新的发展征程。 作为中国智能网联汽车和自动驾驶信息安全行业…...

Kaldi语音识别技术(七) ----- 训练GMM
Kaldi语音识别技术(七) ----- GMM 文章目录Kaldi语音识别技术(七) ----- GMM训练GMMtrain_mono.sh 用于训练GMM训练GMM—生成文件训练GMM—final模型查看训练GMM—final.occs查看训练GMM—对齐信息查看训练GMM—fsts.*.gz查看训练GMM—tree决策树查看align_si.sh 用于对齐训练G…...

Java 集合基础
文章目录一、集合概念二、ArrayList1. 构造方法和添加方法2. 常用方法三、案例演示1. 存储字符串并遍历2. 存储学生对象并遍历3. 键盘录入学生对象并遍历一、集合概念 编程的时候如果要存储多个数据,使用长度固定的数组存储格式,不一定满足我们的需要&a…...

Day896.MySql的kill命令 -MySQL实战
MySql的kill命令 Hi,我是阿昌,今天学习记录的是关于MySql的kill命令的内容。 在 MySQL 中有两个 kill 命令: 一个是 kill query 线程 id,表示终止这个线程中正在执行的语句;一个是 kill connection 线程 id&#…...

L2-010 排座位
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。 输入格式࿱…...

C++的完美讲解,还不快来看看?
目录 简介: 创建C程序: Windows编译简介: Hello,C World! 简介: C融合了3中不同的编程传统:C语言代表的过程性传统、C在C语言基础上添加的类代表的面向对象语言的传统以及C模板支持的通用编程传统。一般来说,计算机语言…...

C语言学习_DAY_5_循环结构while和for语句【C语言学习笔记】
高质量博主,点个关注不迷路🌸🌸🌸! 目录 I. 案例引入 II. while语句 III. do while语句 IV. for语句 前言: 书接上回,判断结构已经解决,接下来是另一种很重要的结构:循环结构的实…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...