项目中遇到了点击按钮重复提交的问题,防止重复点击首先是想到的是给点击事件一个定时,下次触发的条件是要距离上一次点击的时间大于N秒的之后才能再执行。

// 防重复点击函数
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方法

<Button title="点击按钮" onPress={() => preventRepeatPress.onPress(() => fun())} /> 

在点击的时候可以传入设置间隔时间进行单独控制

() => preventRepeatPress.onPress(() => onCredit(),5000)

第二使用 setTimeout 函数

需要注意的是 timeout 需要放在全局也就是函数外部,否则的话每次调用 timeout 都会重新初始化导致防抖函数失去效果。

定义一个名为 debouncePlus 的函数,它接受一个函数 func,一个等待时间 wait(默认为 1000 毫秒)和一个 immediate 参数

// 定义一个变量 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

/**
 * 防抖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;
}

有好的思路欢迎评论交流

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部