这两个 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 的配置如下:
防抖的 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 库里的防抖函数做简单介绍。
- 主要逻辑是使用一个定时器计时,定时器到期则清空定时器。用这层逻辑讲功能函数做一层包裹。
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 时,函数会立即执行一次。
- 增加了对立即执行功能的支持,相关逻辑需要联动修改。功能函数立即执行后,定时器到期则不再执行。
这样就构成了一个能支持大部分场景的 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