UNPKG

1.97 kBJavaScriptView Raw
1import { useMemo, useRef } from 'react';
2import useMounted from './useMounted';
3import useWillUnmount from './useWillUnmount';
4
5/*
6 * Browsers including Internet Explorer, Chrome, Safari, and Firefox store the
7 * delay as a 32-bit signed integer internally. This causes an integer overflow
8 * when using delays larger than 2,147,483,647 ms (about 24.8 days),
9 * resulting in the timeout being executed immediately.
10 *
11 * via: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
12 */
13const MAX_DELAY_MS = 2 ** 31 - 1;
14function setChainedTimeout(handleRef, fn, timeoutAtMs) {
15 const delayMs = timeoutAtMs - Date.now();
16 handleRef.current = delayMs <= MAX_DELAY_MS ? setTimeout(fn, delayMs) : setTimeout(() => setChainedTimeout(handleRef, fn, timeoutAtMs), MAX_DELAY_MS);
17}
18
19/**
20 * Returns a controller object for setting a timeout that is properly cleaned up
21 * once the component unmounts. New timeouts cancel and replace existing ones.
22 *
23 *
24 *
25 * ```tsx
26 * const { set, clear } = useTimeout();
27 * const [hello, showHello] = useState(false);
28 * //Display hello after 5 seconds
29 * set(() => showHello(true), 5000);
30 * return (
31 * <div className="App">
32 * {hello ? <h3>Hello</h3> : null}
33 * </div>
34 * );
35 * ```
36 */
37export default function useTimeout() {
38 const isMounted = useMounted();
39
40 // types are confused between node and web here IDK
41 const handleRef = useRef();
42 useWillUnmount(() => clearTimeout(handleRef.current));
43 return useMemo(() => {
44 const clear = () => clearTimeout(handleRef.current);
45 function set(fn, delayMs = 0) {
46 if (!isMounted()) return;
47 clear();
48 if (delayMs <= MAX_DELAY_MS) {
49 // For simplicity, if the timeout is short, just set a normal timeout.
50 handleRef.current = setTimeout(fn, delayMs);
51 } else {
52 setChainedTimeout(handleRef, fn, Date.now() + delayMs);
53 }
54 }
55 return {
56 set,
57 clear,
58 handleRef
59 };
60 }, []);
61}
\No newline at end of file