@supunlakmal/hooks
Version:
A collection of reusable React hooks
67 lines • 2.77 kB
JavaScript
import { useMemo, useRef } from 'react';
import { useUnmountEffect } from '../side-effects-lifecycle/useUnmountEffect';
/**
* Makes passed function throttled, otherwise acts like `useCallback`.
*
* @param callback Function that will be throttled.
* @param deps Dependencies list when to update callback.
* @param delay Throttle delay.
* @param noTrailing If `noTrailing` is true, callback will only execute every
* `delay` milliseconds, otherwise, callback will be executed one final time
* after the last throttled-function call.
* @returns Returns a throttled version of the function.
*/
export const useThrottledCallback = (callback, deps, wait, noTrailing = false) => {
const timerRef = useRef(null);
const lastCall = useRef(undefined);
if (typeof wait !== 'number') {
console.error('Wait must be a number.');
return () => { };
}
if (typeof callback !== 'function') {
console.error('Callback must be a function.');
return () => { };
}
useUnmountEffect(() => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
});
return useMemo(() => {
let delay = wait;
const execute = (context, args) => {
lastCall.current = undefined;
callback.apply(context, args);
setTimeout(() => {
// If trailing execution is not disabled, call callback with last
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
// received arguments and context
if (!noTrailing && lastCall.current) {
execute(lastCall.current.this, lastCall.current.args);
lastCall.current = undefined;
}
}, delay);
};
const wrapped = function (...args) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (timerRef.current) {
// If we cant execute callback immediately - save its arguments and
// context to execute it when delay is passed
lastCall.current = { args, this: this };
return;
}
execute(this, args);
timerRef.current = setTimeout(() => {
timerRef.current = null;
}, delay);
};
Object.defineProperties(wrapped, {
length: { value: callback.length },
name: { value: `${callback.name || 'anonymous'}__throttled__${delay}` },
});
return wrapped;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wait, noTrailing, callback, ...deps]);
};
//# sourceMappingURL=useThrottledCallback.js.map