一、起因
lodash在react hooks中没成功,每次react函数调用都会产生一个新的timer实例同时旧的timer实例还存在,并没有clear掉失去了debounce的意义
二、自己动手写
在搞清楚debounce函数的原理之后,自己结合网上的写法实现了一版如下
/**
* @desc: 防抖函数
* @param {*} fn
* @param {*} delay
*/
function useDebounce(fn: Function, delay: number) {
const {current} = useRef({fn, timer: null as unknown as NodeJS.Timeout});
useEffect(function() {
current.fn = fn;
}, [current.fn, fn]);
return useCallback(function(...args: any[]) {
if (current.timer) {
clearTimeout(current.timer as unknown as number);
}
current.timer = setTimeout(() => {
current.fn.call(current.fn, ...args);
}, delay);
}, [current.fn, current.timer, delay]);
}
useRef返回相当于一个{current: ...}的plain object,但是和正常这样每轮render之后直接显式创建的区别在于,每轮render之后的useRef返回的plain object都是同一个,只是里面的current发生变化
而且,当里面的current发生变化的时候并不会引起render
通过useRef创建了一个对象引用,初始值为fn和timer,它会在这个函数组件的生命周期内一直存在,拿这个做引用标识是否存在计时器,
useEffect是react hooks的副作用函数,执行在渲染之后,详细介绍可以看官网https://zh-hans.reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
每次return后current都会更新,current一更新,就触发useEffect重新赋值fn
useCallback是根据依赖项是否变化返回一个新函数,如下图,current更新后,每次先判断timer是否存在,这是timer是缓存中的timer会被清除掉,赋予新current一个新timer
这样,如果触发fn函数停止的话就会触发最新的settimeout
const debounce = useDebounce(fn, 1000); //延迟1秒执行
debounce在input onchange事件中作为回调函数出现,这样就实现了hooks函数防抖。
如果错误,欢迎指正!