@supunlakmal/hooks
Version:
A collection of reusable React hooks
91 lines • 3.07 kB
JavaScript
import { useEffect, useMemo, useRef } from 'react';
/**
* Like `useRef`, but it returns immutable ref that contains actual value.
*
* @param value
*/
const useSyncedRef = (value) => {
const ref = useRef(value);
ref.current = value;
return useMemo(() => Object.freeze({
get current() {
return ref.current;
},
}), []);
};
/**
* Run effect only when component is unmounted.
*
* @param effect Effector to run on unmount
*/
const useUnmountEffect = (effect) => {
const effectRef = useSyncedRef(effect);
useEffect(() => () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
effectRef.current();
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]);
};
/**
* Makes passed function debounced, otherwise acts like `useCallback`.
*
* @param callback Function that will be debounced.
* @param deps Dependencies list when to update callback. It also replaces invoked
* callback for scheduled debounced invocations.
* @param delay Debounce delay.
* @param maxWait The maximum time `callback` is allowed to be delayed before
* it's invoked. 0 means no max wait.
*/
export const useDebouncedCallback = (callback, deps, delay, maxWait = 0) => {
const timeout = useRef(undefined);
const waitTimeout = useRef(undefined);
const cb = useRef(callback);
const lastCall = useRef(undefined);
const clear = () => {
if (timeout.current) {
clearTimeout(timeout.current);
timeout.current = undefined;
}
if (waitTimeout.current) {
clearTimeout(waitTimeout.current);
waitTimeout.current = undefined;
}
};
// Cancel scheduled execution on unmount
useUnmountEffect(clear);
useEffect(() => {
cb.current = callback;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return useMemo(() => {
const execute = () => {
clear();
if (!lastCall.current) {
return;
}
const context = lastCall.current;
lastCall.current = undefined;
cb.current.apply(context.this, context.args);
};
const wrapped = function (...args) {
if (timeout.current) {
clearTimeout(timeout.current);
}
lastCall.current = { args, this: this };
// Plan regular execution
timeout.current = setTimeout(execute, delay);
// Plan maxWait execution if required
if (maxWait > 0 && !waitTimeout.current) {
waitTimeout.current = setTimeout(execute, maxWait);
}
};
Object.defineProperties(wrapped, {
length: { value: callback.length },
name: { value: `${callback.name || 'anonymous'}__debounced__${delay}` },
});
return wrapped;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [delay, maxWait, ...deps]);
};
//# sourceMappingURL=useDebouncedCallback.js.map