实现思路
防抖(Debounce)的核心逻辑是:延迟执行目标函数,若在延迟期间函数再次被触发,则重置延迟时间,直到延迟结束后才执行。
实现 useDebounce Hook 需包含以下关键点:
- 使用
useRef保存定时器 ID,避免每次渲染丢失; - 接收目标值/函数和延迟时间作为参数;
- 清理上一次的定时器,确保只执行最后一次触发的逻辑;
- 组件卸载时清除定时器,避免内存泄漏。
完整实现代码
版本1:防抖值(最常用,如输入框防抖)
适用于需要对「值」进行防抖的场景(比如搜索框输入后延迟更新搜索关键词):
jsx
import { useState, useEffect, useRef } from 'react';
/**
* 防抖 Hook(针对值)
* @param {any} value - 需要防抖的目标值
* @param {number} delay - 防抖延迟时间(毫秒),默认500ms
* @returns {any} 防抖后的最终值
*/
function useDebounce(value, delay = 500) {
// 保存防抖后的最终值
const [debouncedValue, setDebouncedValue] = useState(value);
// 保存定时器ID,避免每次渲染重置
const timerRef = useRef(null);
useEffect(() => {
// 每次value变化时,先清除上一次的定时器
clearTimeout(timerRef.current);
// 重新设置定时器,延迟更新防抖值
timerRef.current = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// 组件卸载/依赖变化时清除定时器(清理副作用)
return () => {
clearTimeout(timerRef.current);
};
}, [value, delay]); // 依赖:目标值 + 延迟时间
return debouncedValue;
}
// 用法示例:搜索框防抖
function SearchInput() {
const [inputValue, setInputValue] = useState('');
// 获取防抖后的输入值(延迟500ms更新)
const debouncedSearchValue = useDebounce(inputValue, 500);
// 监听防抖值变化,执行搜索逻辑(仅在输入停止500ms后触发)
useEffect(() => {
if (debouncedSearchValue) {
console.log('执行搜索:', debouncedSearchValue);
// 这里可调用接口请求搜索数据
}
}, [debouncedSearchValue]);
return (
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="输入关键词搜索(防抖500ms)"
/>
);
}
export default SearchInput;
版本2:防抖函数(针对方法)
适用于需要对「函数执行」进行防抖的场景(比如按钮点击、窗口resize):
jsx
import { useCallback, useRef } from 'react';
/**
* 防抖 Hook(针对函数)
* @param {Function} fn - 需要防抖的目标函数
* @param {number} delay - 防抖延迟时间(毫秒),默认500ms
* @returns {Function} 防抖后的函数
*/
function useDebounceFn(fn, delay = 500) {
// 保存定时器ID
const timerRef = useRef(null);
// 使用useCallback缓存函数,避免每次渲染返回新函数
const debouncedFn = useCallback(
(...args) => {
// 清除上一次的定时器
if (timerRef.current) {
clearTimeout(timerRef.current);
}
// 重置定时器,延迟执行目标函数
timerRef.current = setTimeout(() => {
fn(...args); // 传递参数给目标函数
}, delay);
},
[fn, delay]
);
// 提供清除定时器的方法(可选,比如手动取消防抖)
const cancel = useCallback(() => {
clearTimeout(timerRef.current);
timerRef.current = null;
}, []);
return { debouncedFn, cancel };
}
// 用法示例:按钮点击防抖
function DebounceButton() {
// 定义目标函数
const handleClick = (msg) => {
console.log('按钮点击触发:', msg);
// 这里可执行提交表单、请求接口等逻辑
};
// 获取防抖后的函数
const { debouncedFn: debouncedClick } = useDebounceFn(handleClick, 800);
return (
<button onClick={() => debouncedClick('测试防抖')}>
点击触发(防抖800ms)
</button>
);
}
代码关键解释
- useRef 保存定时器ID:
timerRef不会因组件重新渲染而重置,确保能准确清除上一次的定时器; - useEffect 处理副作用:在值变化时触发防抖逻辑,组件卸载时清理定时器,避免内存泄漏;
- useCallback 缓存函数:针对防抖函数版本,避免每次渲染返回新的函数(比如传递给子组件时,防止子组件不必要的重渲染);
- 参数灵活性:两个版本都支持自定义延迟时间,函数版本还支持传递参数给目标函数。
总结
useDebounce分两种核心场景:防抖值(输入框)和 防抖函数(按钮/事件),可根据需求选择;- 核心逻辑是「清除旧定时器 + 重置新定时器」,确保只有最后一次触发的逻辑会执行;
- 必须在组件卸载时清除定时器,避免内存泄漏(通过 useEffect 返回清理函数/手动 cancel 方法)。