function debounce(fun, delay) {
return function (args) {
let that = this
let _args = args
clearTimeout(fun.id)
fun.id = setTimeout(function () {
fun.call(that, _args)
}, delay)
}
}
复制代码
首先,基于react useEffect实现一个useUpdateEffect,在防抖和节流hook的实现中都会用到:react
import { useEffect, useRef } from 'react';
const useUpdateEffect: typeof useEffect = (effect, deps) => {
const isMounted = useRef(false);
useEffect(() => {
if (!isMounted.current) {
isMounted.current = true;
} else {
return effect();
}
}, deps);
};
export default useUpdateEffect;
复制代码
接着,基于react useCallback、useUpdateEffect实现函数防抖useDebounceFn:git
import { DependencyList, useCallback, useEffect, useRef } from 'react';
import useUpdateEffect from '../useUpdateEffect';
type noop = (...args: any[]) => any;
export interface ReturnValue<T extends any[]> {
run: (...args: T) => void;
cancel: () => void;
}
function useDebounceFn<T extends any[]>(fn: (...args: T) => any, wait: number): ReturnValue<T>;
function useDebounceFn<T extends any[]>(
fn: (...args: T) => any,
deps: DependencyList,
wait: number,
): ReturnValue<T>;
function useDebounceFn<T extends any[]>(
fn: (...args: T) => any,
deps: DependencyList | number,
wait?: number,
): ReturnValue<T> {
const _deps: DependencyList = (Array.isArray(deps) ? deps : []) as DependencyList;
const _wait: number = typeof deps === 'number' ? deps : wait || 0;
const timer = useRef<any>();
const fnRef = useRef<noop>(fn);
fnRef.current = fn;
const cancel = useCallback(() => {
if (timer.current) {
clearTimeout(timer.current);
}
}, []);
const run = useCallback(
(...args: any) => {
cancel();
timer.current = setTimeout(() => {
fnRef.current(...args);
}, _wait);
},
[_wait, cancel],
);
useUpdateEffect(() => {
run();
return cancel;
}, [..._deps, run]);
useEffect(() => cancel, []);
return {
run,
cancel,
};
}
export default useDebounceFn;
复制代码
如何使用useDebounceFn这个防抖hook,举个例子:github
效果:快速点击button会频繁调用 run,但只会在全部点击完成 500ms 后执行一次相关函数。浏览器
import React, { useState } from 'react';
import { Button } from 'antd';
import { useDebounceFn } from '../useDebounceFn';
export default () => {
const [value, setValue] = useState(0);
const { run } = useDebounceFn(() => {
setValue(value + 1);
}, 500);
return (
<div>
<p
style={{
marginTop: 16,
}}
>
{' '}
Clicked count: {value}{' '}
</p>
<Button onClick={run}>Click fast!</Button>
</div>
);
};
复制代码
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
复制代码
import { DependencyList, useCallback, useEffect, useRef } from 'react';
import useUpdateEffect from '../useUpdateEffect';
type noop = (...args: any[]) => any;
export interface ReturnValue<T extends any[]> {
run: (...args: T) => void;
cancel: () => void;
}
function useThrottleFn<T extends any[]>(fn: (...args: T) => any, wait: number): ReturnValue<T>;
function useThrottleFn<T extends any[]>(
fn: (...args: T) => any,
deps: DependencyList,
wait: number,
): ReturnValue<T>;
function useThrottleFn<T extends any[]>(
fn: (...args: T) => any,
deps: DependencyList | number,
wait?: number,
): ReturnValue<T> {
const _deps: DependencyList = (Array.isArray(deps) ? deps : []) as DependencyList;
const _wait: number = typeof deps === 'number' ? deps : wait || 0;
const timer = useRef<any>();
const fnRef = useRef<noop>(fn);
fnRef.current = fn;
const currentArgs = useRef<any>([]);
const cancel = useCallback(() => {
if (timer.current) {
clearTimeout(timer.current);
}
timer.current = undefined;
}, []);
const run = useCallback(
(...args: any) => {
currentArgs.current = args;
if (!timer.current) {
timer.current = setTimeout(() => {
fnRef.current(...currentArgs.current);
timer.current = undefined;
}, _wait);
}
},
[_wait, cancel],
);
useUpdateEffect(() => {
run();
}, [..._deps, run]);
useEffect(() => cancel, []);
return {
run,
cancel,
};
}
export default useThrottleFn;
复制代码
如何使用useThrottleFn这个节流hook,举个例子:bash
效果:快速点击button会频繁调用 run,但只会每隔 500ms 执行一次相关函数。antd
import React, { useState } from 'react';
import { Button } from 'antd';
import { useThrottleFn } from '../useThrottleFn';
export default () => {
const [value, setValue] = useState(0);
const { run } = useThrottleFn(() => {
setValue(value + 1);
}, 500);
return (
<div>
<p
style={{
marginTop: 16,
}}
>
{' '}
Clicked count: {value}{' '}
</p>
<Button onClick={run}>Click fast!</Button>
</div>
);
};
复制代码