React中的防抖

659 阅读2分钟

背景

输入框,在输入的过程中搜索匹配的内容下拉框展示。配合change事件搜索匹配结果会导致搜索很频繁。自然而然想到使用防抖避免频繁查询。

防抖不生效

function Com() {
  const [schoolValue, setSchoolValue] = useState('')

  const debounced = (fn: Function) => {
    let timeout: number;
    return function () {
      let context = this;
      let args = arguments;

      if (timeout) {
        clearTimeout(timeout)
      };
      timeout = setTimeout(() => {
        fn.apply(context, args)
      }, 3000)
    }
  };

  const searchSchool = debounced(() => {
    console.log('进行搜索操作:--->', schoolValue)
  });
  
  useEffect(() => {
    searchSchool();
  }, [schoolValue]);

  return <input value={schoolValue} onChange={e => setSchoolValue(e.target.value)} placeholder='请输入' />
}

由于输入框的value绑定的是schoolValue,所以需要在数据更改的时候(也就是change的时候)需要setSchoolValue,不然会导致输入框不能输入内容。

但是setSchoolValue会导致组件重新renderrender之后会导致debounced重新声明,timeout自然也被重新赋值。之前的timeout没有清除,后续一并触发,所以没有达到防抖的效果。

清除之前的timeout


function Com() {
  const [schoolValue, setSchoolValue] = useState('')
  let timeout: number;

  const debounced = (fn: Function) => {
    return function () {
      let context = this;
      let args = arguments;

      if (timeout) {
        clearTimeout(timeout)
      };
      timeout = setTimeout(() => {
        fn.apply(context, args)
      }, 3000)
    }
  };

  const searchSchool = debounced(() => {
    console.log('进行搜索操作:--->', schoolValue)
  });

  useEffect(() => {
    searchSchool();
    return () => {
      clearTimeout(timeout)//清除timeout
    };
  }, [schoolValue]);

  return <input value={schoolValue} onChange={e => setSchoolValue(e.target.value)} placeholder='请输入' />
}


export default Com;


通过ref

function Com() {
  const [schoolValue, setSchoolValue] = useState('')
  let timeout = useRef<number>();

  const debounced = (fn: Function) => {
    return function () {
      let context = this;
      let args = arguments;

      if (timeout.current) {
        clearTimeout(timeout.current)
      };
      timeout.current = setTimeout(() => {
        fn.apply(context, args)
      }, 3000)
    }
  };

  const searchSchool = debounced(() => {
    console.log('进行搜索操作:--->', schoolValue)
  });

  useEffect(() => {
    searchSchool();
  }, [schoolValue]);

  return <input value={schoolValue} onChange={e => setSchoolValue(e.target.value)} placeholder='请输入' />
}


export default Com;

因为在不同的renderref总是指向同一个引用地址,所有的操作都是在这一个地址上操作的。

自定义 useDebounce


// 定义一个防抖函数
function useDebounce(callback: Function, delay: number) {
  const [timer, setTimer] = useState<number>();

  return function (...args: any[]) {
    clearTimeout(timer);
    const newTimer = setTimeout(() => {
      callback(...args);
    }, delay);
    setTimer(newTimer);
  };
}



function Com() {
  const [schoolValue, setSchoolValue] = useState('')

  // 使用防抖函数
  const debouncedSearch = useDebounce((value: string) => {
    console.log("发送请求:", value);
  }, 1000);

  useEffect(() => {
    debouncedSearch(schoolValue)
  }, [schoolValue])

  return <input value={schoolValue} onChange={e => setSchoolValue(e.target.value)} placeholder='请输入' />
}


export default Com;