项目中遇到了点击按钮重复提交的问题,防止重复点击首先是想到的是给点击事件一个定时,下次触发的条件是要距离上一次点击的时间大于N秒的之后才能再执行。
typescript
// 防重复点击函数
export const preventRepeatPress = {
lastPressTi1me: 0, // 上次点击时间
repoTime: 2000, // 默认间隔时间2000
onPress(callback: () => void, waitTime: number = 2000) {
let currTime = Date.now();
if (currTime - this.lastPressTime > this.repoTime) {
this.lastPressTime = currTime;
this.repoTime = waitTime;
callback && callback();
}
},
};
在组件上的点击函数onPress
中使用preventRepeatPress
方法
typescript
<Button title="点击按钮" onPress={() => preventRepeatPress.onPress(() => fun())} />
在点击的时候可以传入设置间隔时间进行单独控制
typescript
() => preventRepeatPress.onPress(() => onCredit(),5000)
第二使用 setTimeout 函数
需要注意的是 timeout 需要放在全局也就是函数外部,否则的话每次调用 timeout 都会重新初始化导致防抖函数失去效果。
定义一个名为 debouncePlus 的函数,它接受一个函数 func,一个等待时间 wait(默认为 1000 毫秒)和一个 immediate 参数
typescript
// 定义一个变量 timeout,用于存储定时器的标识,变量作用域需要在函数外部
let timeout: string | number | NodeJS.Timeout | null | undefined = null;
export const debouncePlus = (
func: { apply: (arg0: any, arg1: IArguments) => void; },
wait: number | undefined = 1000,
immediate: any
) => {
// 变量 result,用于存储函数执行的结果
let result: any;
// 定义一个内部函数 debounced,它将作为防抖后的函数被返回
let debounced = function (this: any) {
// 获取当前函数执行的上下文(this 指向)
let context = this;
// 获取传递给当前函数的参数
let args = arguments;
// 如果定时器已经存在,清除之前的定时器
if (timeout) clearTimeout(timeout);
// 如果 immediate 为真
if (immediate) {
// 如果 timeout 为 null,说明是第一次调用,应该立即执行函数
let callNow = !timeout;
// 设置一个新的定时器,在等待时间之后将 timeout 置为 null
timeout = setTimeout(function () {
timeout = null;
}, wait);
// 如果应该立即执行,则调用原始函数,并将结果存储在 result 中
if (callNow) result = func.apply(context, args);
}
// 如果 immediate 为假
else {
// 设置一个新的定时器,在等待时间之后执行原始函数
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
// 返回函数执行的结果(如果是 immediate 模式,可能是立即执行的结果;否则可能是 null)
return result;
};
// 给 debounced 函数添加一个 cancel 方法,用于取消正在等待执行的定时器
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
// 返回防抖后的函数
return debounced;
};
第三使用 hook
typescript
/**
* 防抖hook
*
const { run } = useDebounceFn(
() => {setValue(value + 1)},
{wait: 500,},
);
*/
import { useEffect, useMemo, useRef } from 'react';
import { debouncePlus } from './debounce-plus';
const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
const isFunction = (value: unknown): value is (...args: any) => any => typeof value === 'function';
interface DebounceOptions {
wait?: number;
leading?: boolean;
trailing?: boolean;
maxWait?: number;
}
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(
() =>
debouncePlus(
(...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;
export 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 function useLatest<T>(value: T) {
const ref = useRef(value);
ref.current = value;
return ref;
}
有好的思路欢迎评论交流