Spaces:
Runtime error
Runtime error
/** | |
* Copyright (c) Meta Platforms, Inc. and affiliates. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
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); | |
/** | |
* The following function's callback gets throttled when the time between two | |
* executions is less than a threshold. | |
* | |
* The threshold is calculated linearly by multiplying the initial delay | |
* and the number of times the button has been throttled. The button can be | |
* throttled up to numThrottles times. | |
* | |
* The function has an optional flag - enableThrottling - which allows a callsite | |
* to optionally disable throttling. This is useful in cases where throttling may | |
* not be necessary. (e.g. for the Track & Play button, we would only like to | |
* throttle after a stream is aborted.) | |
*/ | |
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, | |
}; | |
} | |