import { useRef, useCallback, useEffect, useMemo } from "react"; type DebouncedCallback = ((...args: Args) => void) & { flush: () => void; cancel: () => void; }; /** * Debounced callback — ruft `callback` erst auf, wenn `delay` ms * ohne erneuten Aufruf vergangen sind. Perfekt für Auto-Save. */ export function useDebouncedCallback( callback: (...args: Args) => void, delay: number, ): DebouncedCallback { const timeoutRef = useRef | null>(null); const callbackRef = useRef(callback); const argsRef = useRef(null); // Callback-Ref aktuell halten ohne neu zu rendern useEffect(() => { callbackRef.current = callback; }, [callback]); // Cleanup bei Unmount useEffect(() => { return () => { if (timeoutRef.current) clearTimeout(timeoutRef.current); }; }, []); const cancel = useCallback(() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } argsRef.current = null; }, []); const flush = useCallback(() => { if (!timeoutRef.current) { return; } clearTimeout(timeoutRef.current); timeoutRef.current = null; const args = argsRef.current; argsRef.current = null; if (args) { callbackRef.current(...args); } }, []); return useMemo(() => { const debouncedCallback = ((...args: Args) => { argsRef.current = args; if (timeoutRef.current) clearTimeout(timeoutRef.current); timeoutRef.current = setTimeout(() => { timeoutRef.current = null; const nextArgs = argsRef.current; argsRef.current = null; if (nextArgs) { callbackRef.current(...nextArgs); } }, delay); }) as DebouncedCallback; debouncedCallback.flush = flush; debouncedCallback.cancel = cancel; return debouncedCallback; }, [cancel, delay, flush]); }