防抖
让一个函数在触发后,等待指定时间再执行;如果在等待期间函数再次被触发,则重新计时
标准实现
/**
* 防抖函数
* @param {Function} func - 需要防抖的目标函数
* @param {number} wait - 等待时间(毫秒),默认300ms
* @returns {Function} - 包装后的防抖函数
*/
function debounce(func, wait = 300) {
// 保存定时器ID,用于清除计时
let timeoutId = null;
// 返回包装后的防抖函数
return function (...args) {
// 保存当前上下文(比如DOM事件中的this)
const context = this;
// 核心逻辑:每次触发时,先清除之前的定时器,重新计时
if (timeoutId) clearTimeout(timeoutId);
// 新建定时器,等待指定时间后执行目标函数
timeoutId = setTimeout(() => {
// 执行原函数,传递上下文和参数,避免this丢失、参数丢失
func.apply(context, args);
// 执行后清空定时器ID(非必需,但更规范)
timeoutId = null;
}, wait);
};react hook封装
import { useCallback, useRef } from 'react';
/**
* React 防抖 Hook
* @param {Function} func - 需要防抖的目标函数
* @param {number} wait - 等待时间(毫秒),默认300ms
* @returns {Function} - 包装后的防抖函数
*/
function useDebounce(func, wait = 300) {
// 用 useRef 保存定时器ID(避免每次渲染重置)
// 其次是我们在settimeout中用了这个变量,为了避免闭包陷阱,还是加上ref
const timeoutIdRef = useRef(null);
// 用 useRef 保存最新的函数引用(避免依赖变化导致防抖失效)
// 其次是我们在settimeout和usecallback中用了这个变量,为了避免闭包陷阱,还是加上ref
const funcRef = useRef(func);
// 每次渲染更新函数引用,保证拿到最新的func
funcRef.current = func;
// 用 useCallback 缓存防抖函数,避免组件重渲染时重新创建
const debouncedFunc = useCallback((...args) => {
// 保存当前上下文(React 中通常是组件实例/事件对象)
const context = this;
// 清除之前的定时器,重新计时
if (timeoutIdRef.current) {
clearTimeout(timeoutIdRef.current);
}
// 新建定时器
timeoutIdRef.current = setTimeout(() => {
// 执行最新的函数引用
funcRef.current.apply(context, args);
// 执行后清空定时器ID
timeoutIdRef.current = null;
}, wait);
}, [wait]);
// 清理函数:组件卸载/防抖函数更新时,清除未执行的定时器
const cancelDebounce = useCallback(() => {
if (timeoutIdRef.current) {
clearTimeout(timeoutIdRef.current);
timeoutIdRef.current = null;
}
}, []);
// 返回防抖函数和取消方法
return [debouncedFunc, cancelDebounce];
}
export default useDebounce;