react接口防抖处理
防抖(Debounce)概念
防抖是一种优化技术,用于限制函数的执行频率。当一个函数被频繁调用时(比如用户输入、窗口大小调整等),防抖可以确保函数只在最后一次调用后的一段时间内执行一次,避免不必要的重复执行。
生活中的例子
想象你在电梯门前,当有人要进入电梯时,电梯门会延迟关闭,如果在这期间又有人要进来,电梯门会重新开始延迟计时。只有当一段时间内没有人再进来时,电梯门才会真正关闭。这就是防抖的原理。
代码详解
import { useCallback, useRef } from 'react';/*** 防抖Hook - 用于延迟执行函数调用,避免函数被频繁触发* * 防抖原理:当函数被触发时,设置一个延迟定时器,如果在延迟时间内函数再次被触发,* 则清除之前的定时器并重新设置,直到延迟时间到达后才真正执行函数* * @param {Function} callback - 需要防抖的回调函数* @param {number} delay - 防抖延迟时间(毫秒)* @returns {Function} - 防抖后的函数* * @example* // 创建一个防抖搜索函数,延迟500ms执行* const debouncedSearch = useDebounce(async (query) => {* const result = await searchAPI(query);* return result;* }, 500);* * // 使用防抖函数* const handleSearch = async (value) => {* try {* // 只有在用户停止输入500ms后才会真正调用searchAPI* const result = await debouncedSearch(value);* console.log(result);* } catch (error) {* console.error(error);* }* };* * // 应用场景:* // 1. 搜索框输入实时搜索(避免每次输入都发送请求)* // 2. 窗口大小调整事件处理* // 3. 按钮点击防止重复提交* // 4. 表单验证(避免每次输入都进行验证)*/
export const useDebounce = (callback, delay) => {// 使用useRef保存定时器引用,确保在多次渲染间保持同一引用// 这样可以在每次调用时清除之前的定时器const timeoutRef = useRef(null);// 使用useCallback缓存返回的函数,避免不必要的重新创建// 依赖项为callback和delay,当它们变化时才重新创建函数return useCallback((...args) => {// 清除之前的定时器,防止函数执行// 这是防抖的关键步骤,确保只有最后一次调用会生效if (timeoutRef.current) {clearTimeout(timeoutRef.current);}// 返回Promise以支持异步操作// 这样调用方可以使用async/await处理异步结果return new Promise((resolve, reject) => {// 设置新的定时器,在延迟时间到达后执行回调函数timeoutRef.current = setTimeout(async () => {try {// 执行传入的回调函数,并传入所有参数// 使用async/await支持异步回调函数const result = await callback(...args);// 成功执行后,通过resolve返回结果resolve(result);} catch (error) {// 如果回调函数执行出错,通过reject抛出错误reject(error);}}, delay);});}, [callback, delay]);
};export default useDebounce;
1. Hook 签名和文档
export const useDebounce = (callback, delay) => {
这个 Hook 接收两个参数:
- callback : 需要防抖的函数
- delay : 延迟时间(毫秒)
2. 定时器引用
const timeoutRef = useRef(null);
使用 useRef 保存定时器引用,确保在多次渲染之间保持对同一个定时器的引用。这是防抖实现的关键,因为我们需要能够清除之前的定时器。
3. 缓存返回函数
return useCallback((...args) => {// 函数实现
}, [callback, delay]);
使用 useCallback 缓存返回的函数,避免在每次重新渲染时创建新函数。只有当 callback 或 delay 变化时才会重新创建。
4. 防抖核心逻辑
// 清除之前的定时器
if (timeoutRef.current) {clearTimeout(timeoutRef.current);
}// 设置新的定时器
timeoutRef.current = setTimeout(async () => {const result = await callback(...args);resolve(result);
}, delay);
这部分是防抖的核心实现:
- 每次函数被调用时,首先清除之前的定时器
- 然后设置一个新的定时器,在延迟时间后执行回调函数
- 如果在延迟时间内函数再次被调用,会重新执行第1步和第2步
5. Promise 支持
return new Promise((resolve, reject) => {timeoutRef.current = setTimeout(async () => {try {const result = await callback(...args);resolve(result);} catch (error) {reject(error);}}, delay);
});
通过返回 Promise,这个 Hook 支持异步回调函数,调用方可以使用 async/await 处理异步结果。
实际使用场景
1. 搜索框输入防抖
const debouncedSearch = useDebounce(async (query) => {const result = await searchAPI(query);return result;
}, 500);const handleSearch = async (value) => {try {// 用户快速输入时,只会发送最后一次请求const result = await debouncedSearch(value);setSearchResults(result);} catch (error) {console.error('搜索失败:', error);}
};
2. 窗口大小调整
const debouncedResize = useDebounce((size) => {// 处理窗口大小调整逻辑updateLayout(size);
}, 300);useEffect(() => {const handleResize = () => {debouncedResize({ width: window.innerWidth, height: window.innerHeight });};window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);
}, [debouncedResize]);
工作流程图解
用户输入: a ------------------ ab ------------------- abc ------>↑ ↑ ↑调用1 调用2 调用3| | |清除定时器 清除定时器 清除定时器| | |设置定时器T1 设置定时器T2 设置定时器T3| | |T1被清除 T2被清除 T3执行回调| | |+---------------------+----------------------↓执行一次回调函数
优势
- 性能优化:减少不必要的函数调用,降低系统负载
- 用户体验:避免频繁的网络请求或界面更新
- 灵活性:支持同步和异步回调函数
- 易用性:封装成 Hook,使用简单方便
这个防抖 Hook 是一个非常实用的工具,可以有效提升应用的性能和用户体验。