UNPKG

@qazuor/react-hooks

Version:

A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti

994 lines (974 loc) 29.5 kB
// src/hooks/useBoolean.ts import { useCallback, useState } from "react"; function useBoolean(initialValue = false) { const [value, setValue] = useState(initialValue); const setTrue = useCallback(() => setValue(true), []); const setFalse = useCallback(() => setValue(false), []); const toggle = useCallback(() => setValue((v) => !v), []); const setValueDirect = useCallback((newValue) => setValue(newValue), []); return { value, setTrue, setFalse, toggle, setValue: setValueDirect }; } // src/hooks/useClickOutside.ts import { useCallback as useCallback2, useEffect } from "react"; function useClickOutside(ref, handler, { enabled = true, eventType = "mousedown" } = {}) { const handleClickOutside = useCallback2( (event) => { if (!ref.current || ref.current.contains(event.target)) { return; } handler(event); }, [ref, handler] ); useEffect(() => { if (!enabled) { return; } document.addEventListener(eventType, handleClickOutside); return () => { document.removeEventListener(eventType, handleClickOutside); }; }, [ref, handler, enabled, eventType, handleClickOutside]); } // src/hooks/useCopyToClipboard.ts import { useCallback as useCallback3, useEffect as useEffect2, useRef, useState as useState2 } from "react"; function useCopyToClipboard() { const [state, setState] = useState2({ copied: false, error: null }); const timeoutRef = useRef(null); const copy = useCallback3(async (text) => { try { if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } await navigator.clipboard.writeText(text); setState({ copied: true, error: null }); timeoutRef.current = window.setTimeout(() => { setState((prev) => ({ ...prev, copied: false })); }, 2e3); } catch (err) { setState({ copied: false, error: err }); } }, []); const reset = useCallback3(() => { if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } setState({ copied: false, error: null }); }, []); useEffect2(() => { return () => { if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } }; }, []); return { copy, reset, ...state }; } // src/hooks/useDebounce.ts import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react"; function useDebounce(value, delay, immediate = false) { const [debouncedValue, setDebouncedValue] = useState3(value); const timeoutRef = useRef2(null); useEffect3(() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } if (immediate) { setDebouncedValue(value); return; } timeoutRef.current = window.setTimeout(() => { setDebouncedValue(value); }, delay); return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }; }, [value, delay, immediate]); return debouncedValue; } // src/hooks/useHandledInterval.ts import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react"; function useHandledInterval({ callback, delay, random = false, autoStart = true, minDelay = 0 }) { const callbackRef = useRef3(callback); const [currentDelay, setCurrentDelay] = useState4(delay); const [isRunning, setIsRunning] = useState4(false); const intervalId = useRef3(null); const nextIntervalDelay = useRef3(null); const getRandomDelay = useCallback4(() => { if (!random) { return currentDelay; } const range = currentDelay - minDelay; return Math.floor(Math.random() * range) + minDelay; }, [currentDelay, random, minDelay]); const clear = useCallback4(() => { if (intervalId.current) clearInterval(intervalId.current); intervalId.current = null; nextIntervalDelay.current = null; }, []); const start = useCallback4(() => { if (intervalId.current) return; setIsRunning(true); nextIntervalDelay.current = getRandomDelay(); intervalId.current = window.setInterval(() => { callbackRef.current(); if (random) { clear(); nextIntervalDelay.current = getRandomDelay(); intervalId.current = window.setInterval(callbackRef.current, nextIntervalDelay.current); } }, nextIntervalDelay.current); }, [random, getRandomDelay, clear]); const pause = useCallback4(() => { setIsRunning(false); clear(); }, [clear]); const reset = useCallback4(() => { pause(); start(); }, [pause, start]); const setDelay = useCallback4( (newDelay) => { setCurrentDelay(newDelay); if (isRunning) { clear(); start(); } }, [isRunning, clear, start] ); useEffect4(() => { callbackRef.current = callback; }, [callback]); useEffect4(() => { if (autoStart) { start(); } return () => clear(); }, [autoStart, start, clear]); return { isRunning, start, pause, reset, setDelay }; } // src/hooks/useIdleness.ts import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react"; function useIdleness({ timeout, events = ["mousemove", "keydown", "wheel", "touchstart"], startImmediately = true, onIdleChange }) { const [idle, setIdle] = useState5(false); const [isMonitoring, setIsMonitoring] = useState5(startImmediately); const timerId = useRef4(null); const mounted = useRef4(true); const eventsRef = useRef4(events); const clearTimer = useCallback5(() => { if (timerId.current) { clearTimeout(timerId.current); timerId.current = null; } }, []); const setIdleState = useCallback5( (newState) => { if (mounted.current && idle !== newState) { setIdle(newState); onIdleChange?.(newState); } }, [idle, onIdleChange] ); const resetTimer = useCallback5(() => { clearTimer(); setIdleState(false); if (isMonitoring) { timerId.current = setTimeout(() => { setIdleState(true); }, timeout); } }, [timeout, isMonitoring, clearTimer, setIdleState]); const start = useCallback5(() => { setIsMonitoring(true); resetTimer(); resetTimer(); }, [resetTimer]); const stop = useCallback5(() => { setIsMonitoring(false); clearTimer(); setIdleState(false); }, [clearTimer, setIdleState]); useEffect5(() => { if (isMonitoring) { eventsRef.current.forEach((evt) => window.addEventListener(evt, resetTimer)); timerId.current = setTimeout(() => { setIdleState(true); }, timeout); } return () => { clearTimer(); eventsRef.current.forEach((evt) => window.removeEventListener(evt, resetTimer)); }; }, [isMonitoring, resetTimer, clearTimer, timeout, setIdleState]); useEffect5( () => () => { mounted.current = false; }, [] ); return { isIdle: idle, start, stop, reset: resetTimer }; } // src/hooks/useInterval.ts import { useCallback as useCallback6, useEffect as useEffect6, useRef as useRef5, useState as useState6 } from "react"; function useInterval({ callback, delay, runImmediately = false, autoStart = true }) { const savedCallback = useRef5(callback); const [isRunning, setIsRunning] = useState6(false); const intervalId = useRef5(null); useEffect6(() => { savedCallback.current = callback; }, [callback]); const cleanup = useCallback6(() => { if (intervalId.current !== null) { clearInterval(intervalId.current); intervalId.current = null; } }, []); const start = useCallback6(() => { if (delay === null) { setIsRunning(false); return; } if (intervalId.current !== null) { return; } setIsRunning(true); if (runImmediately) { savedCallback.current(); } intervalId.current = window.setInterval(() => { savedCallback.current(); }, delay); }, [delay, runImmediately]); const pause = useCallback6(() => { cleanup(); setIsRunning(false); }, [cleanup]); const restart = useCallback6(() => { cleanup(); start(); }, [cleanup, start]); useEffect6(() => { if (autoStart) { start(); } return cleanup; }, [autoStart, start, cleanup]); return { isRunning, start, pause, restart }; } // src/hooks/useLocalStorage.ts import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef6, useState as useState7 } from "react"; function useLocalStorage(key, initialValue, options = {}) { const { serializer = JSON.stringify, deserializer = JSON.parse, onError = console.error, syncTabs = false } = options; const mounted = useRef6(true); const [storedValue, setStoredValue] = useState7(() => { try { const item = window.localStorage.getItem(key); if (item) { return deserializer(item); } window.localStorage.setItem(key, serializer(initialValue)); return initialValue; } catch (error) { onError(error); return initialValue; } }); const setValue = useCallback7( (value) => { setStoredValue((prev) => { try { const newValue = value instanceof Function ? value(prev) : value; window.localStorage.setItem(key, serializer(newValue)); return newValue; } catch (error) { onError(error); return prev; } }); }, [key, serializer, onError] ); const handleStorageChange = useCallback7( (event) => { if (event.key === key && event.newValue !== null && mounted.current) { try { const newValue = deserializer(event.newValue); setStoredValue(newValue); } catch (error) { onError(error); } } }, [key, deserializer, onError] ); useEffect7(() => { if (syncTabs) { window.addEventListener("storage", handleStorageChange); return () => { window.removeEventListener("storage", handleStorageChange); }; } }, [syncTabs, handleStorageChange]); useEffect7( () => () => { mounted.current = false; }, [] ); return [storedValue, setValue]; } // src/hooks/useLockBodyScroll.ts import { useCallback as useCallback8, useEffect as useEffect8, useState as useState8 } from "react"; var isTestEnv = () => true; function useLockBodyScroll({ preservePosition = true, lockImmediately = true, additionalStyles = {} } = {}) { const [isLocked, setIsLocked] = useState8(lockImmediately); const [originalStyles, setOriginalStyles] = useState8({}); const [scrollPosition, setScrollPosition] = useState8(0); const applyStyles = useCallback8((styles) => { Object.entries(styles).forEach(([key, value]) => { document.body.style[key] = value?.toString() ?? ""; }); }, []); const saveScrollPosition = useCallback8(() => { if (preservePosition) { setScrollPosition(window.pageYOffset); } }, [preservePosition]); const restoreScrollPosition = useCallback8(() => { if (preservePosition) { try { if (!isTestEnv()) { window.scrollTo(0, scrollPosition); } } catch (error) { if (!isTestEnv()) { console.error(error); } } } }, [preservePosition, scrollPosition]); const lock = useCallback8(() => { if (!isLocked) { saveScrollPosition(); const originalStyle = {}; ["overflow", "position", "top", "width"].forEach((prop) => { originalStyle[prop] = document.body.style[prop]; }); Object.keys(additionalStyles).forEach((key) => { originalStyle[key] = document.body.style[key]; }); setOriginalStyles(originalStyle); const lockStyles = { overflow: "hidden", position: "fixed", top: `-${scrollPosition}px`, width: "100%", ...additionalStyles }; applyStyles(lockStyles); setIsLocked(true); } }, [isLocked, scrollPosition, additionalStyles, saveScrollPosition, applyStyles]); const unlock = useCallback8(() => { if (isLocked) { applyStyles(originalStyles); restoreScrollPosition(); setIsLocked(false); } }, [isLocked, originalStyles, restoreScrollPosition, applyStyles]); const toggle = useCallback8(() => { if (isLocked) { unlock(); } else { lock(); } }, [isLocked, lock, unlock]); useEffect8(() => { if (lockImmediately) { lock(); if (isTestEnv()) { document.body.style.overflow = "hidden"; document.body.style.position = "fixed"; document.body.style.top = scrollPosition > 0 ? `-${scrollPosition}px` : "-0px"; document.body.style.width = "100%"; Object.entries(additionalStyles).forEach(([key, value]) => { document.body.style[key] = value; }); } } return unlock; }, [lockImmediately, lock, unlock, scrollPosition, additionalStyles]); return { isLocked, lock, unlock, toggle }; } // src/hooks/useLogger.ts import { useCallback as useCallback9, useEffect as useEffect9, useRef as useRef7 } from "react"; function useLogger(label, value, options = {}) { const { level = "info", timestamp = true, enabled = true, formatter } = options; const prevValue = useRef7(null); const isFirstRender = useRef7(true); const getTimestamp = useCallback9(() => { if (!timestamp) return ""; return `[${(/* @__PURE__ */ new Date()).toISOString()}] `; }, [timestamp]); const formatMessage = useCallback9( (msg) => { if (formatter) { return formatter(label, msg); } return `${getTimestamp()}[${label}] ${JSON.stringify(msg)}`; }, [label, getTimestamp, formatter] ); const log = useCallback9(() => { if (!enabled) return; const message = formatMessage(value); switch (level) { case "warn": console.warn(message); break; case "error": console.error(message); break; case "debug": console.debug(message); break; default: console.info(message); } }, [enabled, level, value, formatMessage]); useEffect9(() => { if (isFirstRender.current || value !== prevValue.current) { log(); isFirstRender.current = false; prevValue.current = value; } }, [value, log]); return log; } // src/hooks/useMeasure.ts import { useCallback as useCallback10, useEffect as useEffect10, useRef as useRef8, useState as useState9 } from "react"; function useMeasure() { const [size, setSize] = useState9({ width: 0, height: 0 }); const observerRef = useRef8({ observer: null, element: null }); const ref = useCallback10((node) => { if (!node) return; if (observerRef.current.observer) { observerRef.current.observer.disconnect(); } const observer = new ResizeObserver(([entry]) => { setSize({ width: entry.contentRect.width, height: entry.contentRect.height }); }); observerRef.current = { observer, element: node }; observer.observe(node); return () => observer.disconnect(); }, []); useEffect10(() => { return () => { if (observerRef.current.observer) { observerRef.current.observer.disconnect(); } }; }, []); return { ref, size }; } // src/hooks/useMediaQuery.ts import { useCallback as useCallback11, useEffect as useEffect11, useState as useState10 } from "react"; function useMediaQuery(query, { ssrSafe = true, ssrDefaultValue = false, watchImmediately = true } = {}) { const [shouldListen, setShouldListen] = useState10(watchImmediately); const [matches, setMatches] = useState10(() => { if (ssrSafe && typeof window === "undefined") { return ssrDefaultValue; } if (typeof window !== "undefined" && window.matchMedia) { try { return window.matchMedia(query).matches; } catch (e) { console.error("Failed to get initial media query match:", query, e); return ssrDefaultValue; } } return ssrDefaultValue; }); const handleChange = useCallback11((e) => { setMatches(e.matches); }, []); const startWatching = useCallback11(() => { if (typeof window !== "undefined" && window.matchMedia !== void 0) { setShouldListen(true); } else { console.warn("Cannot start watching media query: window.matchMedia is not available."); } }, []); const stopWatching = useCallback11(() => { setShouldListen(false); }, []); useEffect11(() => { let mediaQueryList = null; let handler = null; if (shouldListen && typeof window !== "undefined" && window.matchMedia !== void 0) { try { mediaQueryList = window.matchMedia(query); setMatches(mediaQueryList.matches); handler = handleChange; if (mediaQueryList.addEventListener) { mediaQueryList.addEventListener("change", handler); } else { mediaQueryList.addListener(handler); } } catch (e) { console.error("Failed to set up media query listener:", query, e); setShouldListen(false); } } return () => { if (mediaQueryList && handler) { try { if (mediaQueryList.removeEventListener) { mediaQueryList.removeEventListener("change", handler); } else { mediaQueryList.removeListener(handler); } } catch (e) { console.error("Failed to remove media query listener during cleanup:", query, e); } } }; }, [query, shouldListen, handleChange]); useEffect11(() => { if (ssrSafe && typeof window !== "undefined" && window.matchMedia !== void 0) { try { setMatches(window.matchMedia(query).matches); } catch (e) { console.error("Failed to update matches state after hydration:", query, e); } } }, [query, ssrSafe]); return { matches, startWatching, stopWatching }; } // src/hooks/useNetworkState.ts import { useCallback as useCallback12, useEffect as useEffect12, useState as useState11 } from "react"; function useNetworkState() { const [state, setState] = useState11(() => ({ online: navigator.onLine, ...navigator.connection || {} })); const updateNetworkInfo = useCallback12(() => { const connection = navigator.connection; setState({ online: navigator.onLine, downlink: connection?.downlink, downlinkMax: connection?.downlinkMax, effectiveType: connection?.effectiveType, rtt: connection?.rtt, saveData: connection?.saveData, type: connection?.type }); }, []); useEffect12(() => { const connection = navigator.connection; window.addEventListener("online", updateNetworkInfo); window.addEventListener("offline", updateNetworkInfo); if (connection) { connection?.addEventListener?.("change", updateNetworkInfo); } return () => { window.removeEventListener("online", updateNetworkInfo); window.removeEventListener("offline", updateNetworkInfo); if (connection) { connection?.removeEventListener?.("change", updateNetworkInfo); } }; }, [updateNetworkInfo]); return { ...state, checkConnection: updateNetworkInfo }; } // src/hooks/usePageLeave.ts import { useCallback as useCallback13, useEffect as useEffect13, useState as useState12 } from "react"; function usePageLeave(options = {}) { const { threshold = 0, enabled = true, onLeave, onReturn } = options; const [hasLeft, setHasLeft] = useState12(false); const [isEnabled, setIsEnabled] = useState12(enabled); const handleMouseOut = useCallback13( (e) => { if (!isEnabled) return; if (e.clientY <= threshold) { setHasLeft(true); onLeave?.(); } }, [threshold, isEnabled, onLeave] ); const handleMouseOver = useCallback13(() => { if (!isEnabled) return; if (hasLeft) { setHasLeft(false); onReturn?.(); } }, [isEnabled, hasLeft, onReturn]); const enable = useCallback13(() => { setIsEnabled(true); }, []); const disable = useCallback13(() => { setIsEnabled(false); }, []); useEffect13(() => { if (isEnabled) { document.addEventListener("mouseout", handleMouseOut); document.addEventListener("mouseover", handleMouseOver); } return () => { document.removeEventListener("mouseout", handleMouseOut); document.removeEventListener("mouseover", handleMouseOver); }; }, [isEnabled, handleMouseOut, handleMouseOver]); return { hasLeft, enable, disable }; } // src/hooks/useQueue.ts import { useCallback as useCallback14, useMemo, useState as useState13 } from "react"; function useQueue(initialValues = [], options = {}) { const { maxSize, onFull, onEmpty, equalityFn = (a, b) => a === b } = options; const [queue, setQueue] = useState13(initialValues); const enqueue = useCallback14( (item) => { setQueue((q) => { if (maxSize && q.length >= maxSize) { onFull?.(); return q; } return [...q, item]; }); }, [maxSize, onFull] ); const dequeue = useCallback14(() => { if (queue.length === 0) { return void 0; } const itemToDequeue = queue[0]; setQueue((currentQueue) => { if (currentQueue.length === 0) return currentQueue; const newQueue = currentQueue.slice(1); if (onEmpty && currentQueue.length > 0 && newQueue.length === 0) { onEmpty(); } return newQueue; }); return itemToDequeue; }, [queue, onEmpty]); const clear = useCallback14(() => { if (onEmpty && queue.length > 0) { onEmpty(); } setQueue([]); }, [onEmpty, queue]); const peek = useCallback14(() => queue[0], [queue]); const peekLast = useCallback14(() => queue[queue.length - 1], [queue]); const contains = useCallback14((item) => queue.some((i) => equalityFn(i, item)), [queue, equalityFn]); const state = useMemo( () => ({ isEmpty: queue.length === 0, size: queue.length, toArray: () => [...queue] }), [queue] ); return { enqueue, dequeue, clear, peek, peekLast, contains, ...state }; } // src/hooks/useSessionStorage.ts import { useCallback as useCallback15, useEffect as useEffect14, useRef as useRef9, useState as useState14 } from "react"; function useSessionStorage(key, initialValue, options = {}) { const { serializer = JSON.stringify, deserializer = JSON.parse, onError = console.error, syncTabs = false } = options; const mounted = useRef9(true); const [storedValue, setStoredValue] = useState14(() => { try { const item = window.sessionStorage.getItem(key); return item ? deserializer(item) : initialValue; } catch (error) { onError(error); return initialValue; } }); const setValue = useCallback15( (value) => { setStoredValue((prev) => { try { const newValue = value instanceof Function ? value(prev) : value; window.sessionStorage.setItem(key, serializer(newValue)); return newValue; } catch (error) { onError(error); return prev; } }); }, [key, serializer, onError] ); const handleStorageChange = useCallback15( (event) => { if (event.key === key && event.newValue !== null && mounted.current) { try { const newValue = deserializer(event.newValue); setStoredValue(newValue); } catch (error) { onError(error); } } }, [key, deserializer, onError] ); useEffect14(() => { if (syncTabs) { window.addEventListener("storage", handleStorageChange); return () => { window.removeEventListener("storage", handleStorageChange); }; } }, [syncTabs, handleStorageChange]); useEffect14( () => () => { mounted.current = false; }, [] ); return [storedValue, setValue]; } // src/hooks/useTimeout.ts import { useCallback as useCallback16, useEffect as useEffect15, useRef as useRef10, useState as useState15 } from "react"; function useTimeout({ callback, delay, autoStart = true }) { const savedCallback = useRef10(() => { }); const timeoutId = useRef10(null); const [isPending, setIsPending] = useState15(false); useEffect15(() => { savedCallback.current = callback; }, [callback]); const clear = useCallback16(() => { if (timeoutId.current !== null) { clearTimeout(timeoutId.current); timeoutId.current = null; } setIsPending(false); }, []); const start = useCallback16(() => { clear(); if (delay !== null) { setIsPending(true); timeoutId.current = window.setTimeout(() => { savedCallback.current(); setIsPending(false); }, delay); } }, [delay, clear]); const reset = useCallback16(() => { clear(); start(); }, [clear, start]); useEffect15(() => { if (autoStart && delay !== null) { start(); } return clear; }, [delay, autoStart, start, clear]); return { isPending, start, cancel: clear, reset }; } // src/hooks/useToggle.ts import { useCallback as useCallback17, useEffect as useEffect16, useRef as useRef11, useState as useState16 } from "react"; function useToggle({ initialValue = false, onChange, persist = false, storageKey = "useToggle" } = {}) { const [value, setValue] = useState16(() => { if (persist) { const stored = localStorage.getItem(storageKey); return stored ? JSON.parse(stored) : initialValue; } return initialValue; }); const mounted = useRef11(true); const updateValue = useCallback17( (newValue) => { if (mounted.current) { setValue(newValue); onChange?.(newValue); if (persist) { localStorage.setItem(storageKey, JSON.stringify(newValue)); } } }, [onChange, persist, storageKey] ); const toggle = useCallback17(() => updateValue(!value), [value, updateValue]); const setTrue = useCallback17(() => updateValue(true), [updateValue]); const setFalse = useCallback17(() => updateValue(false), [updateValue]); useEffect16( () => () => { mounted.current = false; }, [] ); return { value, toggle, setTrue, setFalse, setValue: updateValue }; } // src/hooks/useVisibilityChange.ts import { useCallback as useCallback18, useEffect as useEffect17, useState as useState17 } from "react"; function useVisibilityChange({ onVisible, onHidden, startImmediately = true } = {}) { const [isVisible, setIsVisible] = useState17(!document.hidden); const [isMonitoring, setIsMonitoring] = useState17(startImmediately); const handleVisibilityChange = useCallback18(() => { const newVisibility = !document.hidden; setIsVisible(newVisibility); if (newVisibility) { onVisible?.(); } else { onHidden?.(); } }, [onVisible, onHidden, isMonitoring]); const start = useCallback18(() => setIsMonitoring(true), []); const stop = useCallback18(() => setIsMonitoring(false), []); useEffect17(() => { if (isMonitoring) { document.addEventListener("visibilitychange", handleVisibilityChange); return () => { document.removeEventListener("visibilitychange", handleVisibilityChange); }; } else { document.removeEventListener("visibilitychange", handleVisibilityChange); } }, [isMonitoring, handleVisibilityChange]); return { isVisible, start, stop }; } // src/hooks/useWindowWidth.ts import { useCallback as useCallback19, useEffect as useEffect18, useRef as useRef12, useState as useState18 } from "react"; function useWindowWidth({ debounceDelay = 250, startImmediately = true, initialWidth = typeof window !== "undefined" ? window.innerWidth : 0, onChange } = {}) { const [width, setWidth] = useState18(initialWidth); const [isMonitoring, setIsMonitoring] = useState18(startImmediately); const timeoutRef = useRef12(null); const handleResize = useCallback19(() => { if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } timeoutRef.current = window.setTimeout(() => { const newWidth = window.innerWidth; setWidth(newWidth); onChange?.(newWidth); }, debounceDelay); }, [debounceDelay, onChange]); const start = useCallback19(() => setIsMonitoring(true), []); const stop = useCallback19(() => setIsMonitoring(false), []); useEffect18(() => { if (isMonitoring) { window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } }; } else { window.removeEventListener("resize", handleResize); if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); timeoutRef.current = null; } } }, [isMonitoring, handleResize]); return { width, start, stop }; } export { useBoolean, useClickOutside, useCopyToClipboard, useDebounce, useHandledInterval, useIdleness, useInterval, useLocalStorage, useLockBodyScroll, useLogger, useMeasure, useMediaQuery, useNetworkState, usePageLeave, useQueue, useSessionStorage, useTimeout, useToggle, useVisibilityChange, useWindowWidth };