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