Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | 91x 91x 91x 91x 34x 91x 76x 20x 20x 91x 12x 12x 91x 35x 35x 35x 35x 15x 15x 15x 91x 7x 1x 6x 91x 35x 6x 6x 29x 29x 29x 91x | import { useCallback, useEffect, useRef, useState } from "react";
/**
* Delay type for useTimeout
* - number: delay in milliseconds
* - null | undefined: disable timer
*/
export type TimeoutDelay = number | null | undefined;
/**
* Callback type for useTimeout
*/
export type UseTimeoutCallback = () => void;
/**
* Return type for useTimeout hook
*/
export interface UseTimeoutReturn {
/**
* Reset timer (restart from beginning)
*/
reset: () => void;
/**
* Cancel timer (callback won't execute)
*/
clear: () => void;
/**
* Whether timer is pending
*/
isPending: boolean;
}
/**
* A hook for declarative setTimeout with automatic cleanup and controls.
*
* Provides a safe way to use setTimeout in React components with:
* - Automatic cleanup on unmount (prevents memory leaks)
* - Auto-reset when delay changes
* - Disable timer when delay is null/undefined
* - Always maintains latest callback reference (prevents stale closure)
* - isPending state to check timer status
*
* @param callback - Function to execute after delay
* @param delay - Delay in milliseconds, or null/undefined to disable
* @returns Object containing reset, clear functions and isPending state
*
* @example
* ```tsx
* // Auto-dismissing toast
* function Toast({ message }: { message: string }) {
* const [show, setShow] = useState(true);
*
* useTimeout(() => {
* setShow(false);
* }, 3000);
*
* return show ? <div>{message}</div> : null;
* }
* ```
*
* @example
* ```tsx
* // Debounced auto-save with reset
* function Editor() {
* const [content, setContent] = useState("");
*
* const { reset } = useTimeout(() => {
* saveToServer(content);
* }, 2000);
*
* const handleChange = (value: string) => {
* setContent(value);
* reset(); // Reset timer on every keystroke
* };
*
* return <textarea onChange={(e) => handleChange(e.target.value)} />;
* }
* ```
*
* @example
* ```tsx
* // Conditional execution
* function SessionTimeout({ isLoggedIn }: { isLoggedIn: boolean }) {
* useTimeout(
* () => {
* logout();
* alert("Session expired");
* },
* isLoggedIn ? 30 * 60 * 1000 : null // Only when logged in
* );
*
* return <div>...</div>;
* }
* ```
*/
export function useTimeout(
callback: UseTimeoutCallback,
delay: TimeoutDelay
): UseTimeoutReturn {
const [isPending, setIsPending] = useState<boolean>(false);
const timeoutIdRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const callbackRef = useRef<UseTimeoutCallback>(callback);
// Always keep the latest callback reference
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
// Clear timeout helper
const clearTimeoutHelper = useCallback(() => {
if (timeoutIdRef.current !== null) {
clearTimeout(timeoutIdRef.current);
timeoutIdRef.current = null;
}
}, []);
// Clear function (exposed)
const clear = useCallback(() => {
clearTimeoutHelper();
setIsPending(false);
}, [clearTimeoutHelper]);
// Set timeout helper
const setTimeoutHelper = useCallback(
(delayMs: number) => {
// Clear existing timeout
clearTimeoutHelper();
// Normalize delay: treat negative as 0
const normalizedDelay = Math.max(0, delayMs);
setIsPending(true);
timeoutIdRef.current = setTimeout(() => {
timeoutIdRef.current = null;
setIsPending(false);
callbackRef.current();
}, normalizedDelay);
},
[clearTimeoutHelper]
);
// Reset function (exposed)
const reset = useCallback(() => {
if (delay === null || delay === undefined) {
return;
}
setTimeoutHelper(delay);
}, [delay, setTimeoutHelper]);
// Effect: setup/cleanup timeout when delay changes
useEffect(() => {
// If delay is null or undefined, clear and don't set new timer
if (delay === null || delay === undefined) {
clear();
return;
}
// Set new timeout
setTimeoutHelper(delay);
// Cleanup on unmount or delay change
return () => {
clearTimeoutHelper();
};
}, [delay, setTimeoutHelper, clearTimeoutHelper, clear]);
return {
reset,
clear,
isPending,
};
}
|