【ahooks 源码】useThrottle, useDebounce

这两个 hook 都有用到 ahook 的工具 hook useLatest 与 useUnmount。先来分析下这两个依赖 hook。

useLatest

官方文档给出这个 hook 的作用是:返回当前最新值的 Hook,可以避免闭包问题。

代码实现非常简单,源码如下:

import { useRef } from 'react';

function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;

  return ref;
}

export default useLatest;

用 ref 把 value 包裹了一下。 相当于写了一个全局变量,可以在多次渲染之间共享数据。

useUnmount

作用:在组件卸载(unmount)时执行的 Hook。

源码:

import { useEffect } from 'react';
import useLatest from '../useLatest';
import { isFunction } from '../utils';
import isDev from '../utils/isDev';

const useUnmount = (fn: () => void) => {
  if (isDev) {
    if (!isFunction(fn)) {
      console.error(`useUnmount expected parameter is a function, got ${typeof fn}`);
    }
  }

  const fnRef = useLatest(fn);

  useEffect(
    () => () => {
      fnRef.current();
    },
    [],
  );
};

export default useUnmount;

此处卸载时执行的函数也用 ref 保持,可以避免多次渲染多次创建函数,浪费性能。

useDebounce

useDebounce 分析

useDebounce 底层是用的 loadash 的 debounce 函数来处理防抖。

hook 的配置如下:

【ahooks 源码】useThrottle, useDebounce

防抖的 4 个参数实际上都是 loadash 的 debounce 函数来处理。

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

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

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

  return debounced;
}

useDebounce hook 把主要的处理逻辑放在 useDebounceFn 中,useDebounceFn:

主要防抖逻辑直接用的 lodash 的 debounce,useDebounceFn 中还做了环境判定、缓存函数、防抖参数校验处理、和 hook 卸载时取消防抖几个处理。

import { debounce } from '../utils/lodash-polyfill';
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;

debounce 函数

lodash 的防抖函数写的非常完整,考虑各种场景和边界情况,因此先用 you don’t need loadash underscore 库里的防抖函数做简单介绍。

  1. 主要逻辑是使用一个定时器计时,定时器到期则清空定时器。用这层逻辑讲功能函数做一层包裹。
function debounce(func, wait) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		clearTimeout(timeout);
		timeout = setTimeout(function() {
			timeout = null;
			func.apply(context, args);
		}, wait);
	};
  }

使用防抖时,有些场景会需要函数能立即执行,因此 debounce 函数增加了一个参数: immediate。

immediate 为 true 时,函数会立即执行一次。

  1. 增加了对立即执行功能的支持,相关逻辑需要联动修改。功能函数立即执行后,定时器到期则不再执行。

这样就构成了一个能支持大部分场景的 debounce 函数:

function debounce(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		clearTimeout(timeout);
    // 立即执行逻辑
		if (immediate && !timeout) func.apply(context, args);
		timeout = setTimeout(function() {
			timeout = null;
      // 如果立即执行,则定时器到期不再次执行。
			if (!immediate) func.apply(context, args);
		}, wait);
	};
  }

useThrottle

防抖的作用是在某个时间段内,只执行一次执行的某个功能函数。

useThrottle hook 的源码结构类似与 useDebounce,只是底层的工具函数改成了 loadash 的 throttle

此处与上文介绍 debounce 一致,同样使用 you don’t need loadash underscore 中的 throttle 作为示例:

function throttle(func, timeFrame) {
  var lastTime = 0;
  return function (...args) {
      var now = new Date();
      if (now - lastTime >= timeFrame) {
          func(...args);
          lastTime = now;
      }
  };
}

原文链接:https://juejin.cn/post/7351612144528162857 作者:Tiffany

(0)
上一篇 2024年3月30日 上午10:26
下一篇 2024年3月30日 上午10:32

相关推荐

发表回复

登录后才能评论