|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {useCallback, useState} from 'react'; |
|
|
|
|
|
type ThrottleOptions = { |
|
|
enableThrottling?: boolean; |
|
|
}; |
|
|
type State = { |
|
|
isThrottled: boolean; |
|
|
maxThrottles: boolean; |
|
|
throttle: (callback: () => void, options?: ThrottleOptions) => void; |
|
|
}; |
|
|
|
|
|
export default function useFunctionThrottle( |
|
|
initialDelay: number, |
|
|
numThrottles: number, |
|
|
): State { |
|
|
const [isThrottled, setIsThrottled] = useState<boolean>(false); |
|
|
const [lastClickTime, setLastClickTime] = useState<number | null>(null); |
|
|
const [numTimesThrottled, setNumTimesThrottled] = useState<number>(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const throttle = useCallback( |
|
|
( |
|
|
callback: () => void, |
|
|
options: ThrottleOptions = { |
|
|
enableThrottling: true, |
|
|
}, |
|
|
) => { |
|
|
if (isThrottled) { |
|
|
return; |
|
|
} |
|
|
|
|
|
const currentTime = Date.now(); |
|
|
if (lastClickTime == null) { |
|
|
callback(); |
|
|
setLastClickTime(currentTime); |
|
|
return; |
|
|
} |
|
|
|
|
|
const timeBetweenClicks = currentTime - lastClickTime; |
|
|
const delay = initialDelay * numTimesThrottled; |
|
|
const shouldThrottle = |
|
|
options.enableThrottling && delay > timeBetweenClicks; |
|
|
|
|
|
if (shouldThrottle) { |
|
|
setIsThrottled(true); |
|
|
setTimeout(() => { |
|
|
setIsThrottled(false); |
|
|
}, delay); |
|
|
setNumTimesThrottled(prev => { |
|
|
return prev === numThrottles ? numThrottles : prev + 1; |
|
|
}); |
|
|
} |
|
|
|
|
|
callback(); |
|
|
setLastClickTime(currentTime); |
|
|
}, |
|
|
[initialDelay, numThrottles, isThrottled, lastClickTime, numTimesThrottled], |
|
|
); |
|
|
|
|
|
return { |
|
|
isThrottled, |
|
|
maxThrottles: numTimesThrottled === numThrottles, |
|
|
throttle, |
|
|
}; |
|
|
} |
|
|
|