源码链接:https://github.com/alibaba/ho...

概述

首先,useDebounceFn和useDebounce都是一个用于添加防抖功能的hook,不同之处在于useDebounceFn给函数添加防抖,而useDebounce用来给值添加防抖。

防抖

某些时候,我们会无法控制所编写事件的触发频率;比如搜索引擎的搜索框对于输入内容的实时反馈,以及一些根据实时数据动态更新的组件。如果在短时间内触发大量事件,可能会引起闪烁甚至卡顿。

防抖(Debounce)这个概念是解决此类问题的一种方法(其他的方法比如节流)。

防抖,控制事件只会在:触发该事件,并在固定时间内未触发第二次时,才会执行。若短时间内该事件被连续触发多次,则会在最后一次触发的固定时间后再执行此事件。

useDebounceFn

该hook用来包装所需要的函数,给函数添加防抖动的功能。

简单用法

以下的代码将会在点击按钮并1000毫秒内未再次点击按钮时,将value变更为2。

import { useDebounceFn } from 'ahooks';
import React, { useState } from 'react';

export default () => {
  const [value, setValue] = useState(0);
  const { run } = useDebounceFn(
    (newValue) => {
      setValue(newValue);
    },
    {
      wait: 1000,
    leading
    },
  );

  return (
    <div>
      <p style={{ marginTop: 16 }}> Clicked count: {value} </p>
      <button type="button" onClick={run(2)}>
        chuange to 2
      </button>
    </div>
  );
};

参数说明

配置参数

  • wait:防抖等待时间,默认值为1000ms(number)
  • leading:是否在延迟开始前调用函数,默认为false
  • trailing:是否在延迟开始后调用函数,默认为true
  • maxWait:最大等待时间,单位为ms

结果参数

  • run:触发执行传入hook的函数,参数即为函数需要的参数
  • cancel:取消当前防抖
  • flush:立即调用当前防抖函数

源码分析

isDev

if (isDev) {
    if (!isFunction(fn)) {
      console.error(`useDebounceFn expected parameter is a function, got ${typeof fn}`);
    }
  }

isDev是自定义的一个环境变量,用来判断当前是否处于开发环境。

useLatest

const fnRef = useLatest(fn);
useLatest也是ahooks的一个hook,一般是用来返回某个state的最新值,避免闭包问题(比如上一篇文章中提到过的,useCallback的闭包陷阱)。

debounce

  const debounced = useMemo(
    () =>
      debounce(
        (...args: Parameters<T>): ReturnType<T> => {
          return fnRef.current(...args);
        },
        wait,
        options,
      ),
    [],
  );

debounce是lodash中的方法,用来解决包装函数解决防抖问题。这里使用useMemo缓存debounce这个方法,同样是为了减少消耗。下面封装run、cancel、flush时,也是直接使用debounce的方法。

Parameters是typescript的告诫类型,用于获取函数T的参数类型,而ReturnType则是用来获取函数T的返回类型。

fnRef是使用useLatest封装好的,传入函数的最新值。

useUnmount

 useUnmount(() => {
    debounced.cancel();
  });

useUnmount也是ahooks封装好的hook,用来设置在组件卸载时执行的函数;为了防止内存泄露,当组件卸载时需要取消对debounce的调用。

附上源码


import debounce from 'lodash/debounce';
import { useMemo } from 'react';
import type { DebounceOptions } from '../useDebounce/debounceOptions';
import useLatest from '../useLatest';
import useUnmount from '../useUnmount';
import { isFunction } from '../utils';
import isDev from '../utils/isDev';

type noop = (...args: any[]) => any;

function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions) {
//判断是否开发环境
  if (isDev) {
    if (!isFunction(fn)) {
      console.error(`useDebounceFn expected parameter is a function, got ${typeof fn}`);
    }
  }

  const fnRef = useLatest(fn);

  const wait = options?.wait ?? 1000;

  const debounced = useMemo(
    () =>
      debounce(
        (...args: Parameters<T>): ReturnType<T> => {
          return fnRef.current(...args);
        },
        wait,
        options,
      ),
    [],
  );

  useUnmount(() => {
    debounced.cancel();
  });

  return {
    run: debounced,
    cancel: debounced.cancel,
    flush: debounced.flush,
  };
}

export default useDebounceFn;

useDebounce

分析过useDebounceFn后,useDebounce的源码较为简单,直接用useState创建一个debounced,然后使用useDebounceFn封装它的更新方法setDebounced即可。

useEffect

该hook在每次依赖项改变时会执行,与usecallback不同的是,当依赖项为空时,该hook每次重新渲染都会执行。用在这里,当传入的参数改变时就调用一次debounce的run(当然还是遵循防抖动的规则)

源码

function useDebounce<T>(value: T, options?: DebounceOptions) {
  const [debounced, setDebounced] = useState(value);

  const { run } = useDebounceFn(() => {
    setDebounced(value);
  }, options);

  useEffect(() => {
    run();
  }, [value]);

  return debounced;
}

Non_
1 声望0 粉丝