create-plume-react-project
Version:
CLI to create Plume React Project
69 lines (59 loc) • 2.25 kB
text/typescript
import {
RefObject, useEffect, useRef,
} from 'react';
/**
* Enable to control the timeout in the component where it is used.
* See {@link useTimeout}
*/
export type TimeoutController = {
/**
* Stop and restart the timeout
*/
restartTimeout: () => void,
/**
* Stop the timeout
*/
stopTimeout: () => void,
};
type TimeoutType = ReturnType<typeof setTimeout>;
/**
* This hook enables to use safely {@link setTimeout} in a component.
* The issue using raw {@link setTimeout} is that it can resolve after the component has been unmounted...
* This can create lots of very difficult bugs.
*
* This hook makes sure to stop/unregister the {@link setTimeout} when the component is being unmounted.
* @param callback The function that will be called after {@link delayInMillis} time has passed.
* If the callback changes over time, the callback called after the timeout will be the changed one.
* If the timeout has already passed, the callback changes will have no impact.
* @param delayInMillis The delay in milliseconds after which the {@link callback} will be executed.
* Delay changes after the first call will be ignored.
*
* This hook is a typed and commented version of
* https://github.com/WebDevSimplified/useful-custom-react-hooks/blob/main/src/2-useTimeout/useTimeout.js
*/
export default function useTimeout(callback: () => void, delayInMillis: number) {
// it's important to have a React ref here: it enables to keep a reference to the last version of the callback
const callbackRef: RefObject<() => void> = useRef(callback);
const timeoutIdRef: RefObject<TimeoutType | undefined> = useRef<TimeoutType | undefined>(undefined);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const startTimeout = () => {
timeoutIdRef.current = setTimeout(() => callbackRef.current(), delayInMillis);
};
const stopTimeout = () => {
if (timeoutIdRef.current) {
clearTimeout(timeoutIdRef.current);
}
};
useEffect(() => {
startTimeout();
return stopTimeout;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const restartTimeout = () => {
stopTimeout();
startTimeout();
};
return { restartTimeout, stopTimeout };
}