react接口防抖处理

防抖(Debounce)概念

防抖是一种优化技术,用于限制函数的执行频率。当一个函数被频繁调用时(比如用户输入、窗口大小调整等),防抖可以确保函数只在最后一次调用后的一段时间内执行一次,避免不必要的重复执行。

生活中的例子

想象你在电梯门前,当有人要进入电梯时,电梯门会延迟关闭,如果在这期间又有人要进来,电梯门会重新开始延迟计时。只有当一段时间内没有人再进来时,电梯门才会真正关闭。这就是防抖的原理。

代码详解

javascript 复制代码
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 签名和文档

javascript 复制代码
export const useDebounce = (callback, delay) => {

这个 Hook 接收两个参数:

  • callback : 需要防抖的函数
  • delay : 延迟时间(毫秒)

2. 定时器引用

javascript 复制代码
const timeoutRef = useRef(null);

使用 useRef 保存定时器引用,确保在多次渲染之间保持对同一个定时器的引用。这是防抖实现的关键,因为我们需要能够清除之前的定时器。

3. 缓存返回函数

javascript 复制代码
return useCallback((...args) => {
  // 函数实现
}, [callback, delay]);

使用 useCallback 缓存返回的函数,避免在每次重新渲染时创建新函数。只有当 callback 或 delay 变化时才会重新创建。

4. 防抖核心逻辑

javascript 复制代码
// 清除之前的定时器
if (timeoutRef.current) {
  clearTimeout(timeoutRef.current);
}

// 设置新的定时器
timeoutRef.current = setTimeout(async () => {
  const result = await callback(...args);
  resolve(result);
}, delay);

这部分是防抖的核心实现:

  1. 每次函数被调用时,首先清除之前的定时器
  2. 然后设置一个新的定时器,在延迟时间后执行回调函数
  3. 如果在延迟时间内函数再次被调用,会重新执行第1步和第2步

5. Promise 支持

javascript 复制代码
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. 搜索框输入防抖

javascript 复制代码
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. 窗口大小调整

javascript 复制代码
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]);

工作流程图解

html 复制代码
用户输入:  a ------------------ ab ------------------- abc ------>
          ↑                     ↑                      ↑
        调用1                 调用2                  调用3
          |                     |                      |
      清除定时器             清除定时器              清除定时器
          |                     |                      |
      设置定时器T1          设置定时器T2           设置定时器T3
          |                     |                      |
      T1被清除              T2被清除               T3执行回调
          |                     |                      |
          +---------------------+----------------------↓
                                                执行一次回调函数

优势

  1. 性能优化:减少不必要的函数调用,降低系统负载
  2. 用户体验:避免频繁的网络请求或界面更新
  3. 灵活性:支持同步和异步回调函数
  4. 易用性:封装成 Hook,使用简单方便

这个防抖 Hook 是一个非常实用的工具,可以有效提升应用的性能和用户体验。

相关推荐
梦想CAD控件3 分钟前
在线CAD开发包结构与功能说明
前端·javascript·vue.js
张拭心8 分钟前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
时光不负努力8 分钟前
typescript常用的dom 元素类型
前端·typescript
小怪点点14 分钟前
大文件切片上传
前端
时光不负努力14 分钟前
TS 常用工具类型
前端·javascript·typescript
SuperEugene16 分钟前
Vue状态管理扫盲篇:Vuex 到 Pinia | 为什么大家都在迁移?核心用法对比
前端·vue.js·面试
张拭心18 分钟前
Android 17 来了!新特性介绍与适配建议
android·前端
徐小夕23 分钟前
pxcharts-vue:一款专为 Vue3 打造的开源多维表格解决方案
前端·vue.js·github
Hilaku23 分钟前
我会如何考核一个在简历里大谈 AI 提效的高级前端?
前端·javascript·面试
进击的尘埃36 分钟前
Vue3 中 emit 能 await 吗?事件机制里的异步陷阱
javascript