React中防抖节流研究
以下是代码。此处为了通用的节流防抖在React中形成闭包,是将节流防抖第一次调用函数在componentWillMount时赋值给了state中的变量
import React from 'react'
class PropsParent extends React.Component {
constructor(props) {
super(props)
this.state = {
msgText: '请输入消息内容',
throttleFn: '', // 节流函数引用
throttleFn1: '', // 防抖函数引用
throttleFn2: '', // 防抖函数引用
throttleFn3: '' // 防抖函数引用
}
}
render() {
return (
<div>
<input placeholder='输入改变传递给子组件的内容' onChange= { (e) => {this.changeText(e)}}></input>
<div>{this.state.msgText}</div>
</div>
)
}
UNSAFE_componentWillMount() {
// 节流防抖设置
// 为了使throttle形成闭包,只调用一次throttle,之后由其引用throttleFn来调用
this.setState({
debounceFn: this.debounce(this.handleChangeText, 1000),
throttleFn1: this.throttle1(this.handleChangeText, 1000),
throttleFn2: this.throttle2(this.handleChangeText, 1000),
throttleFn3: this.throttle3(this.handleChangeText, 1000)
})
}
// 防抖函数 一定时间段内没有再触发事件,事件处理函数才会执行一次
debounce(fn, delay) {
let that = this
return function() {
let args = arguments;
clearTimeout(fn.id)
fn.id = setTimeout(() => {
fn.call(that, args)
}, delay)
}
}
// 节流函数(时间戳) 每delay时间才执行一次
// 优点 第一次立即执行
// 缺点 最后一次执行时间到最后一次触发事件时间间隔小于delay,那么将不会执行最后一次事件
throttle1(fn,delay) {
let that = this
// 形成闭包后,此数据不变
var prev = Date.now();
return function() {
var now = Date.now();
let args = arguments;
if(now - prev >= delay) {
fn.call(that, args)
prev = Date.now();
}
}
}
// 节流函数(定时器) 每delay时间才执行一次
// 缺点 第一次不会立即执行 最后一次触发事件若是在timer未被清空的这段事件内,就不会被执行
throttle2(fn,delay) {
let that = this
// 形成闭包后,此数据不变
var timer = null
return function() {
let args = arguments;
if(!timer) {
timer = setTimeout(() => {
fn.call(that, args)
timer = null
}, delay)
}
}
}
// 节流函数(定时器)
// 时间戳+定时器,当第一次触发事件时马上执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
// 在节流函数内部使用开始时间startTime、当前时间curTime与delay来计算剩余时间remaining,
// 当remaining<=0时表示该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数和每隔delay时间执行一次事件处理函数)。
// 如果还没到时间的话就设定在remaining时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。
// 当然在remaining这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。
throttle3(fn,delay) {
let that = this
// 形成闭包后,此数据不变
var startTime = Date.now();
var timer = null
return function() {
let args = arguments;
var curTime = Date.now();
// 在remaining这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态
var remaining = delay - (curTime -startTime)
clearTimeout(timer)
if(remaining <= 0) {
// 保证了第一次触发事件就能立即执行事件处理函数和每隔delay时间执行一次事件处理函数)
fn.call(that, args)
startTime = Date.now();
} else {
// 保证了最后一次触发事件后还能再执行一次事件处理函数
timer = setTimeout(() => {
fn.call(that, args)
timer = null
},remaining)
}
}
}
handleChangeText(args) {
console.log(args)
this.setState({
msgText: args[0]
})
}
changeText(e) {
this.state.debounceFn(e.target.value)
// this.state.throttleFn1(e.target.value)
// this.state.throttleFn2(e.target.value)
// this.state.throttleFn3(e.target.value)
}
}
export default PropsParent
补充内容:
在函数式组件中使用节流防抖,需要使用useRef或useMemo缓存返回的节流防抖函数,否则每次更新页面会重新执行生成的节流防抖函数