14114

11/18/2025

import { useEffect, useRef } from 'react';

interface UseDebounceWithCleanupOptions { delay?: number; // Automatically handles pending requests on unmount. // Use shouldFlush for immediate execution on specific conditions. shouldFlush?: boolean; }

interface UseDebounceWithCleanupParams { callback: () => void; options?: UseDebounceWithCleanupOptions; }

export const useDebounceWithCleanup = ( { callback, options = {} }: UseDebounceWithCleanupParams, dependency: readonly unknown[] ) => { const { delay = 500, shouldFlush = false } = options; const timeoutRef = useRef<number | null>(null); const callbackRef = useRef(callback); const hasPendingRef = useRef(false);

useEffect(() => { callbackRef.current = callback; }, [callback]);

useEffect(() => { if (shouldFlush && timeoutRef.current) { clearTimeout(timeoutRef.current); callbackRef.current(); timeoutRef.current = null; hasPendingRef.current = false; } }, [shouldFlush]);

useEffect(() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); }

timeoutRef.current = window.setTimeout(() => {
  callbackRef.current();
  timeoutRef.current = null;
  hasPendingRef.current = false;
}, delay);

hasPendingRef.current = true;

return () => {
  if (timeoutRef.current) {
    clearTimeout(timeoutRef.current);
  }
};

}, [...dependency, delay]);

useEffect(() => { return () => { if (timeoutRef.current && hasPendingRef.current) { clearTimeout(timeoutRef.current); callbackRef.current(); timeoutRef.current = null; hasPendingRef.current = false; } }; }, []); };

© 2025 Mingu Kim. All rights reserved.