useTimeout
Provides imperative control over setTimeout with React-friendly state management. This hook creates a managed timeout system that integrates seamlessly with React's lifecycle. Unlike raw setTimeout, it provides state tracking, automatic cleanup, and safe callback updates without recreating timers.
Key Features
- Manual Control: Explicitly schedule, reschedule, or cancel timeouts
- State Tracking: Check if a timeout is pending with
isIdle() - Safe Updates: Callback updates don't affect running timers
- Auto-cleanup: Prevents memory leaks on unmount
- Reschedule Support: Calling schedule() resets existing timers
Use Cases
- Delayed Actions: Show notifications, hide tooltips, or auto-save
- Debouncing: Implement custom debounce logic with full control
- Timeout Sequences: Chain multiple timeouts with state awareness
- Conditional Delays: Execute actions based on timeout state
- Loading States: Auto-hide loading indicators after delay
Signature
const useTimeout: (callback: Fn, timeout?: number) => UseTimeoutReturn
Parameters
| Name | Type | Description |
|---|---|---|
callback | - | The function to execute after the timeout. Can be updated without affecting running timers |
timeout | - | The delay in milliseconds before executing the callback (default: 0) |
Returns
Object — Control object with three methods:
Examples
Example 1
// Auto-dismiss notification
const Notification = ({ message, duration = 3000 }) => {
const { schedule, cancel } = useTimeout(() => {
setVisible(false);
}, duration);
useEffect(() => {
schedule();
return cancel; // Cleanup on unmount
}, [schedule, cancel]);
return (
<div onMouseEnter={cancel} onMouseLeave={schedule}>
{message}
</div>
);
};
// Loading state with timeout
const DataFetcher = () => {
const [showSkeleton, setShowSkeleton] = useState(false);
const { isIdle, schedule, cancel } = useTimeout(
() => setShowSkeleton(true),
200 // Show skeleton after 200ms
);
const fetchData = async () => {
schedule(); // Start skeleton timer
try {
const data = await api.getData();
if (!isIdle()) {
cancel(); // Cancel if data loads quickly
}
setData(data);
} catch (error) {
cancel();
setError(error);
}
};
};
// Sequential timeouts
const AnimationSequence = () => {
const [stage, setStage] = useState(0);
const stage1Timeout = useTimeout(() => setStage(1), 1000);
const stage2Timeout = useTimeout(() => setStage(2), 1000);
const stage3Timeout = useTimeout(() => setStage(3), 1000);
useEffect(() => {
if (stage === 0) stage1Timeout.schedule();
else if (stage === 1) stage2Timeout.schedule();
else if (stage === 2) stage3Timeout.schedule();
}, [stage]);
};
// Conditional execution based on state
const { isIdle, schedule, cancel } = useTimeout(() => {
if (hasUnsavedChanges) {
saveDocument();
}
}, 5000);
// Reschedule on user activity
const handleUserInput = () => {
cancel();
schedule(); // Reset 5-second timer
};
Playground
// Auto-dismiss notification const Notification = ({ message, duration = 3000 }) => { const { schedule, cancel } = useTimeout(() => { setVisible(false); }, duration); useEffect(() => { schedule(); return cancel; // Cleanup on unmount }, [schedule, cancel]); return ( <div onMouseEnter={cancel} onMouseLeave={schedule}> {message} </div> ); }; // Loading state with timeout const DataFetcher = () => { const [showSkeleton, setShowSkeleton] = useState(false); const { isIdle, schedule, cancel } = useTimeout( () => setShowSkeleton(true), 200 // Show skeleton after 200ms ); const fetchData = async () => { schedule(); // Start skeleton timer try { const data = await api.getData(); if (!isIdle()) { cancel(); // Cancel if data loads quickly } setData(data); } catch (error) { cancel(); setError(error); } }; }; // Sequential timeouts const AnimationSequence = () => { const [stage, setStage] = useState(0); const stage1Timeout = useTimeout(() => setStage(1), 1000); const stage2Timeout = useTimeout(() => setStage(2), 1000); const stage3Timeout = useTimeout(() => setStage(3), 1000); useEffect(() => { if (stage === 0) stage1Timeout.schedule(); else if (stage === 1) stage2Timeout.schedule(); else if (stage === 2) stage3Timeout.schedule(); }, [stage]); }; // Conditional execution based on state const { isIdle, schedule, cancel } = useTimeout(() => { if (hasUnsavedChanges) { saveDocument(); } }, 5000); // Reschedule on user activity const handleUserInput = () => { cancel(); schedule(); // Reset 5-second timer };