重学节流和防抖
防抖
概念理解
防抖就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
举例说明
坐电梯的时候,如果电梯检测到有人进来(触发事件),就会多等待 10 秒,此时如果又有人进来(10秒之内重复触发事件),那么电梯就会再多等待 10 秒。
在上述例子中,电梯在检测到有人进入 10 秒钟之后,才会关闭电梯门开始运行,因此,“函数防抖”的关键在于,在 一个事件 发生 一定时间 之后,才执行 特定动作。
应用场景
在开发过程中遇到的高频率触发事件
mousedown、mousemove、keyup、keydown,resize
input搜索事件,表单提交多次频繁点击
实现方式
第一版(demo1代码)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 1000);
render() {
return <button onClick={this.handleAddCount}>{this.state.count}</button>;
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
func;
}, wait);
};
}
-
问:这个函数用到的主要知识点是什么,这个函数可以使用吗?
举例说明:
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 1000);
render() {
return <button onClick={this.handleAddCount}>{this.state.count}</button>;
}
}
结果:闭包,用不了
原因:this 指向问题
我们可知在setTimeout 中this 指向windows 详情可 MDN看详细说明
JavaScript 环境中内置的 setTimeout() 函数实现和下面的伪代码类似:
function setTimeout(fn,delay) {
// 等待 delay 毫秒
fn(); // <-- 调用位置!
}
第二版(修改this指向问题- demo2代码)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo2 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 5000);
render() {
return <button onClick={this.handleAddCount}>{this.state.count}</button>;
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return function() {
const this_ = this;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this_);
}, wait);
};
}
- 问: 修改好this指向以后,这个函数可以用吗?
结果:可以使用了,但是还有问题
大家看看这个函数还有什么问题?
修改上面部分代码:(demo3)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo3 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce((params: number) => {
console.log(params, 'params');
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
}, 1000);
render() {
return (
<button
onClick={() => {
this.handleAddCount(20);
}}
>
{this.state.count}
</button>
);
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this);
}, wait);
};
}
结果:结果为NaN,我们可以知道,函数的参数丢失了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3B9b1NVk-1629202244062)(C:\Users\wangj\AppData\Local\Temp\1628754204830.png)]
第三版(修改参数丢失问题-demo4)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo4 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce((params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
}, 1000);
render() {
return (
<button
onClick={() => {
this.handleAddCount(20);
}}
>
{this.state.count}
</button>
);
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return (...args: any) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
问:修改号参数丢失问题后,该函数还有什么问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nl6EPABz-1629202244064)(C:\Users\wangj\AppData\Local\Temp\1628754642565.png)]
举例说明(demo5)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo5 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce((params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
return this.state.count;
}, 1000);
handleChange = () => {
this.handleAddCount(1);
console.log(this.handleAddCount(1), 'this.handleAddCount(1);');
};
render() {
return (
<button
onClick={() => {
this.handleChange();
}}
>
{this.state.count}
</button>
);
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return (...args: any) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
通过以上代码我们发现,当前代码无法获取到函数的返回值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WX3VkRtD-1629202244065)(C:\Users\wangj\AppData\Local\Temp\1628760189072.png)]
答:由上方可以看出,函数返回值丢失了
第四版(函数添加返回值)
因为setTimeout 为异步函数的问题和this指向的问题,导致延时器中返回的函数只能是undefined,那么我们怎么来处理这个问题呢?
方法比较多,具体大家可以参考异步函数返回值
在这我主要写两种最常见的方法:回调函数,promise
回调函数(demo6)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
getCount = (params: any) => {
console.log(params, 'params');
};
handleAddCount = debounce(
(params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
return this.state.count;
},
1000,
this.getCount,
);
handleChange = () => {
this.handleAddCount(1);
};
render() {
return (
<button
onClick={() => {
this.handleChange();
}}
>
{this.state.count}
</button>
);
}
}
function debounce(func: any, wait: number, callback: any): any {
let timer: NodeJS.Timeout;
return (...args: any) => {
clearTimeout(timer);
timer = setTimeout(() => {
callback(func.apply(this, args));
}, wait);
};
}
promise (demo7)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce((params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
return this.state.count;
}, 8000);
handleChange = () => {
this.handleAddCount(1).then(params => {
console.log(params, 'params');
});
};
render() {
return (
<button
onClick={() => {
this.handleChange();
}}
>
{this.state.count}
</button>
);
}
}
function debounce(func: any, wait: number): any {
let timer: NodeJS.Timeout;
return (...args) => {
return new Promise(function(resolve) {
clearTimeout(timer);
timer = setTimeout(() => {
resolve(func.apply(this, args));
}, wait);
});
};
}
问:大家觉这一版函数有什么问题?
答:不符合防抖正常情况,不能立即执行
第五版(立即执行问题-demo8)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
getCount = (params: any) => {
console.log(params, 'params');
};
handleAddCount = debounce(
(params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
return this.state.count;
},
5000,
true,
this.getCount,
);
render() {
return (
<button
onClick={() => {
this.handleAddCount(1);
}}
>
{this.state.count}
</button>
);
}
}
// 第五版
function debounce(
func: any,
wait: number,
immediate: boolean,
callback: any,
): any {
let timer: NodeJS.Timeout | null;
return (...args: any) => {
if (timer) clearTimeout(timer);
if (immediate) {
let runNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (runNow) {
callback(func.apply(this, args));
}
} else {
timer = setTimeout(() => {
callback(func.apply(this, args));
}, wait);
}
};
}
第六版(添加防抖取消代码-demo9)
import React, { Component } from 'react';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo9 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = debounce(
(params: number) => {
this.setState({ count: this.state.count + params }, () => {
console.log(this.state.count, 'count');
});
},
5000,
true,
);
handleCancel = () => {
this.handleAddCount.cancel();
};
render() {
return (
<div>
<button
onClick={() => {
this.handleAddCount(1);
}}
>
{this.state.count}
</button>
<button
onClick={() => {
this.handleCancel();
}}
>
取消防抖
</button>
</div>
);
}
}
function debounce(
func: any,
wait: number,
immediate: boolean,
callback?: any,
): any {
let timer: NodeJS.Timeout | null;
const debounced = (...args: any[]) => {
const runFunction = () => {
return callback
? callback(func.apply(this, args))
: func.apply(this, args);
};
if (timer) clearTimeout(timer);
if (immediate) {
let runNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (runNow) {
runFunction();
}
} else {
timer = setTimeout(() => {
runFunction();
}, wait);
}
};
debounced.cancel = function() {
timer && clearTimeout(timer);
timer = null;
};
return debounced;
}
第七版(react hook 防抖实现-demo10)
我们将第六版的函数直接用到react hook 版本中
import React, { useState, useRef, useEffect } from 'react';
const demo10: React.FC<any> = () => {
const [count, setCount] = useState<number>(0);
const handleAddCount = UseDebounce(
() => {
setCount(count => count + 1);
},
3000,
false,
);
return (
<div>
<button onClick={handleAddCount}>count|{count}</button>
</div>
);
};
export default demo10;
// 普通函数
function UseDebounce(
func: any,
wait: number,
immediate: boolean,
callback?: any,
): any {
let timer: NodeJS.Timeout | null;
const debounced = (...args: any[]) => {
const runFunction = () => {
return callback
? callback(func.apply(this, args))
: func.apply(this, args);
};
if (timer) clearTimeout(timer);
if (immediate) {
let runNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (runNow) {
runFunction();
}
} else {
timer = setTimeout(() => {
runFunction();
}, wait);
}
};
debounced.cancel = function() {
timer && clearTimeout(timer);
timer = null;
};
return debounced;
}
发现函数失效了,
注意点:在上面例子中,debounce的第三个函数为false 时,闭包的原因导致,导致定时器timer 未被清理, 从而导致防抖起作用
失效原因分析:
在执行setCount 时,count 改变导致函数重建,延时器timer 被清除,在这我们只需要保证timer不被清除就可以了,我们使用useRef来缓存变量timer
function UseDebounce(
func: any,
wait: number,
immediate: boolean,
callback?: any,
): any {
let timer = useRef<NodeJS.Timeout | null>();
const debounced = (...args: any[]) => {
console.log('渲染');
const runFunction = function() {
console.log('ddd');
return callback
? callback(func.apply(this, args))
: func.apply(this, args);
};
if (timer.current) clearTimeout(timer.current);
if (immediate) {
let runNow = !timer.current;
timer.current = setTimeout(() => {
timer.current = null;
}, wait);
if (runNow) {
runFunction();
}
} else {
timer.current = setTimeout(() => {
runFunction();
}, wait);
}
};
debounced.cancel = function() {
timer.current && clearTimeout(timer.current);
timer.current = null;
};
return debounced;
}
第八版(react hook 优化-demo11)
在demo10中存在UseDebounce 多次生成的情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PdYDGpA1-1629202244067)(C:\Users\wangj\AppData\Local\Temp\1629103502231.png)]
我们怎么解决这种情况呢?
-
借助
useCallback
函数的第二个参数,做依赖更新。 -
借助
useRef
永久存贮,借助useEffect
更新永久存贮。import React, { useState, useCallback, useEffect, useRef } from 'react'; const demo11: React.FC<any> = () => { const [count, setCount] = useState<number>(0); const handleClick = useCallback(data => { console.log(data, 'data'); }, []); const func = React.useCallback(() => { setCount(count => count + 1); return 2; }, []); const handleAddCount = UseDebounce(func, 3000, true, handleClick); return ( <div> <button onClick={handleAddCount}>count|{count}</button> </div> ); }; export default demo11; function UseDebounce( func: any, wait: number, immediate: boolean, callback?: any, ): any { let timer = useRef<NodeJS.Timeout | null>(); const fnRef = useRef<any>(func); useEffect(() => { fnRef.current = func; }, [func]); const timerCancel = function() { if (timer.current) clearTimeout(timer.current); }; console.log('渲染'); function debounced(...args: any[]) { const runFunction = () => { return callback ? callback(fnRef.current.apply(fnRef.current, args)) : fnRef.current.apply(fnRef.current, args); }; timerCancel(); if (immediate) { let runNow = !timer.current; timer.current = setTimeout(() => { timer.current = null; }, wait); if (runNow) { runFunction(); } } else { timer.current = setTimeout(() => { runFunction(); }, wait); } } debounced.cancel = function() { timerCancel(); timer.current = null; }; return useCallback(debounced, [wait, immediate, timerCancel, func]); }
节流
概念理解
节流就是指连续触发事件在 n 秒中只执行一次函数。节流会稀释函数的执行频率
举例说明
坐火车或地铁,过安检的时候,在一定时间(例如10秒)内,只允许一个乘客通过安检入口,以配合安检人员完成安检工作。上例中,每10秒内,仅允许一位乘客通过。
分析可知,“函数节流”的要点在于,在 一定时间 之内,限制 一个动作 只 执行一次 。
应用场景
scroll
事件,每隔一秒计算一次位置信息等- 浏览器播放事件,每个一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)
实现方式
第一版(时间戳版-demo1)
import React, { Component } from 'react';
import '../index.less';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = throttle(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 3000);
render() {
return (
<div className="box" onMouseOver={this.handleAddCount}>
{this.state.count}
</div>
);
}
}
function throttle(func: any, wait: number) {
let previous: number = 0;
return (...args: any) => {
let now = new Date().getTime();
if (now - previous > wait) {
func.apply(this, args);
previous = now;
}
};
}
第二版(定时器版-demo2)
import React, { Component } from 'react';
import '../index.less';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = throttle(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 3000);
render() {
return (
<div className="box" onMouseOver={this.handleAddCount}>
{this.state.count}
</div>
);
}
}
function throttle(func: any, wait: number) {
let timer: NodeJS.Timeout | null = null;
return (...args: any) => {
if (!timer) {
timer = setTimeout(() => {
timer = null;
func.apply(this, args);
}, wait);
}
};
}
现在我们分析一下,上面两版
第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件
所以我们将这两版总结一下,实现一版,鼠标移入能立即执行,但是停止触发的时候还能再次执行一次,代码如下:
第三版(二合一版-demo3)
import React, { Component } from 'react';
import '../index.less';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
handleAddCount = throttle(() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
}, 3000);
render() {
return (
<div className="box" onMouseOver={this.handleAddCount}>
{this.state.count}
</div>
);
}
}
function throttle(func: any, wait: number) {
var previous = 0;
let timer: NodeJS.Timeout | null;
const runFunction = function(...args: any[]) {
previous = new Date().getTime();
timer = null;
func.apply(this, args);
};
const throttled = (...args: any[]) => {
var now = new Date().getTime();
var remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
func.apply(this, args);
} else if (!timer) {
timer = setTimeout(runFunction, remaining);
}
};
return throttled;
}
上面的代码还有许多局限,比如说,我想要实现,鼠标移入不执行,但是停止触发的时候再执行一次的效果,或者鼠标移入立即执行,但是停止触发的时候不再执行的效果。对于这种情况我们应该如何去做呢?
那我们设置个 options 作为第三个参数,然后根据传的值判断到底哪种效果,我们约定:
leading:false 表示禁用第一次执行
trailing: false 表示禁用停止触发的回调
同时在加上函数的回调函数,和节流取消
我们来优化一下代码:
第四版(demo4代码)
import React, { Component } from 'react';
import '../index.less';
type TypeProps = any;
type TypeState = {
count: number;
};
export default class demo1 extends Component<TypeProps, TypeState> {
constructor(props: Readonly<TypeProps>) {
super(props);
this.state = {
count: 0,
};
}
getCount = (data: any) => {
console.log(data, 'data');
};
handleAddCount = throttle(
() => {
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count, 'count');
});
return 2;
},
10000,
{
trailing: false,
},
this.getCount,
);
handleCancel = () => {
this.handleAddCount.cancel();
};
render() {
return (
<div>
<div className="box" onMouseOver={this.handleAddCount}>
{this.state.count}
</div>
<button
onClick={() => {
this.handleCancel();
}}
>
取消节流
</button>
</div>
);
}
}
function throttle(func: any, wait: number, options?: any, callback?: any) {
let previous: number = 0;
let timer: NodeJS.Timeout | null = null;
if (!options) {
options = {};
}
const timerCancel = function() {
if (timer) clearTimeout(timer);
};
const runFunction = (...args: any) => {
previous = options.leading === false ? 0 : new Date().getTime();
timer = null;
callback ? callback(func.apply(this, args)) : func.apply(this, args);
if (!timer) args = null;
};
const throttled = (...args: any) => {
let now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
timerCancel();
previous = now;
callback ? callback(func.apply(this, args)) : func.apply(this, args);
if (!timer) args = null;
} else if (!timer && options.trailing !== false) {
timer = setTimeout(runFunction, remaining);
}
};
throttled.cancel = function() {
timerCancel();
timer = null;
previous = 0;
};
return throttled;
}
我们要注意有这样一个问题:
那就是 leading:false 和 trailing: false 不能同时设置。
如果同时设置的话,比如当你将鼠标移出的时候,因为 trailing 设置为 false,停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会立刻执行,就违反了 leading: false,bug 就出来了,所以,这个 throttle 只有三种用法:
container.onmousemove = throttle(getUserAction, 1000);
container.onmousemove = throttle(getUserAction, 1000, {
leading: false
});
container.onmousemove = throttle(getUserAction, 1000, {
trailing: false
});
第五版(react hook 节流实现-demo5)
import React, { useState, useRef, useEffect, useCallback } from 'react';
const demo5: React.FC<any> = () => {
const [count, setCount] = useState<number>(0);
const handleClick = useCallback(data => {
console.log(data, 'data');
}, []);
const handleAddCount = UseThrottle(
() => {
setCount(count => count + 1);
return 2;
},
3000,
true,
handleClick,
);
const handleCancel = useCallback(() => {
handleAddCount.cancel();
}, []);
return (
<div>
<div className="box" onMouseOver={handleAddCount}>
{count}
</div>
<button onClick={handleCancel}>取消节流</button>
</div>
);
};
export default demo5;
function UseThrottle(func: any, wait: number, options?: any, callback?: any) {
const previous = useRef<number>(0);
const timer = useRef<NodeJS.Timeout | null>();
const fnRef = useRef<any>(func);
if (!options) {
options = {};
}
useEffect(() => {
fnRef.current = func;
}, []);
const runFunction = (...args: any) => {
previous.current = options.leading === false ? 0 : new Date().getTime();
timer.current = null;
callback
? callback(func.apply(fnRef.current, args))
: func.apply(fnRef.current, args);
if (!timer) args = null;
};
const timerCancel = function() {
if (timer.current) clearTimeout(timer.current);
};
const throttled = (...args: any) => {
let now = new Date().getTime();
if (!previous.current && options.leading === false) previous.current = now;
let remaining = wait - (now - previous.current);
if (remaining <= 0 || remaining > wait) {
if (timer.current) {
clearTimeout(timer.current);
timer.current = null;
}
previous.current = now;
callback
? callback(func.apply(fnRef.current, args))
: func.apply(fnRef.current, args);
if (!timer.current) args = null;
} else if (!timer.current && options.trailing !== false) {
timer.current = setTimeout(runFunction, remaining);
}
};
throttled.cancel = function() {
timerCancel();
timer.current = null;
previous.current = 0;
};
return useCallback(throttled, [wait, options, timerCancel, func]);
}
注意点
当我在实现react中触发input 组件中的onChange 事件时去发送一个异步请求,但是发现一直获取不到value 值,代码如下
const onSearchChange = (e) => {
e.persist()
e.preventDefault()
e.stopPropagation()
setSearchValue(e.currentTarget.value)
useDebounce(() => {
setFilterValue(e.currentTarget.value)
}, 500)
}
就是setFilterValue 一直获取不到值,最后经过调查发现了原由,
首先,在 onSearchChange 中,事件触发,就能获取到 event 对象,其中主要就是 event.target 就是当前触发事件的 dom 对象,由于 useDebounce延迟执行,导致了 onSearchChange 函数已经执行完了,进入了 react-dom 中相关一系列操作(进行了一系列复杂的操作),下面给出最关键的 executeDispatchesAndRelease,executeDispatchesAndRelease 方法释放 event.target 的值
/**
* Dispatches an event and releases it back into the pool, unless persistent.
*
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
var executeDispatchesAndRelease = function(event) {
if (event) {
executeDispatchesInOrder(event);
if (!event.isPersistent()) {
event.constructor.release(event);
}
}
};
由于 event 在 useDebounce中作为了参数,内存中没有清除,执行上面的方法 event.target = null; event 为引用类型,一处改变,所有用到的地方都会改变。导致了 useDebounce中 event 也发生了变化。
解决办法:
const onSearchChange = (e) => {
e.persist()
e.preventDefault()
e.stopPropagation()
setSearchValue(e.currentTarget.value)
handleSearchChange(e.currentTarget.value)
}
const handleSearchChange = useDebounce((value) => {
setFilterValue(value)
}, 500)