源码链接: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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。