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

760 lines (738 loc) 28.3 kB
import { RefObject } from 'react'; type UseBooleanReturn = { value: boolean; setTrue: () => void; setFalse: () => void; toggle: () => void; setValue: (value: boolean) => void; }; /** * useBoolean * * @description Manages a boolean state with convenient methods to set it to true, false, or toggle it. * * @param {boolean} [initialValue=false] - Optional initial value. * @returns {UseBooleanReturn} - An object containing: * - `value`: The current boolean value. * - `setTrue()`: Sets the value to `true`. * - `setFalse()`: Sets the value to `false`. * - `toggle()`: Toggles the value between `true` and `false`. * - `setValue()`: Directly sets the value to the given boolean. * * @example * ```ts * const { value, setTrue, setFalse, toggle } = useBoolean(); * setTrue(); // value -> true * toggle(); // value -> false * ``` */ declare function useBoolean(initialValue?: boolean): UseBooleanReturn; type Handler = (event: MouseEvent) => void; interface UseClickOutsideOptions { enabled?: boolean; eventType?: 'mousedown' | 'mouseup' | 'click'; } /** * useClickOutside * * @description Triggers a given handler function when the user clicks outside the referenced element. * Useful for closing dropdowns, modals, or popovers. * * @param {RefObject<HTMLElement>} ref - A React ref object pointing to the element to detect clicks outside of. * @param {Handler} handler - A callback function to execute when a click occurs outside the element. * @param {UseClickOutsideOptions} [options] - Optional configuration object. * @param {boolean} [options.enabled=true] - Whether the click outside detection is enabled. * @param {string} [options.eventType='mousedown'] - The type of mouse event to listen for. * * @example * ```ts * const ref = useRef(null); * useClickOutside(ref, () => { * console.log('Clicked outside the element!'); * }, { * enabled: true, * eventType: 'mousedown' * }); * ``` */ declare function useClickOutside(ref: RefObject<HTMLElement>, handler: Handler, { enabled, eventType }?: UseClickOutsideOptions): void; interface CopyToClipboardState { copied: boolean; error: Error | null; } interface UseCopyToClipboardReturn extends CopyToClipboardState { copy: (text: string) => Promise<void>; reset: () => void; } /** * useCopyToClipboard * * @description Provides a function to copy text to the user's clipboard. It returns * a status indicating whether the copy operation succeeded, and any error if it failed. * * @returns {UseCopyToClipboardReturn} - An object containing: * - `copy`: A function that attempts to write `text` to the clipboard. * - `copied`: A boolean that is `true` if the last copy attempt was successful. * - `error`: An Error object if the last attempt failed, or `null`. * - `reset`: A function to reset the copied and error states. * * @example * ```ts * const { copy, copied, error } = useCopyToClipboard(); * * async function handleCopy() { * await copy('Hello Clipboard'); * if (copied) console.log('Copied successfully!'); * if (error) console.log('Error copying text'); * } * ``` */ declare function useCopyToClipboard(): UseCopyToClipboardReturn; /** * useDebounce * * @description Returns a debounced value that updates only after the specified delay has elapsed * since the last change to the original value. Useful for delaying expensive operations (e.g., API calls) * until the user stops typing or making changes. * * @typeParam T - The type of the value being debounced. * @param {T} value - The input value to debounce. * @param {number} delay - The delay in milliseconds before updating the debounced value. * @param {boolean} [immediate=false] - Whether to update immediately on the first value change. * @returns {T} - The debounced value. * * @example * ```ts * const [searchTerm, setSearchTerm] = useState(''); * const debouncedSearch = useDebounce(searchTerm, 300, true); * * useEffect(() => { * // Perform search or API call with debouncedSearch * }, [debouncedSearch]); * ``` */ declare function useDebounce<T>(value: T, delay: number, immediate?: boolean): T; type UseHandledIntervalOptions = { /** The callback function to run repeatedly. */ callback: () => void; /** Base delay in milliseconds between calls, if `random` is false. */ delay: number; /** If true, each interval is a random duration between 0 and `delay`. */ random?: boolean; /** If true, the interval starts automatically. Default is true. */ autoStart?: boolean; /** Minimum delay when using random intervals. Default is 0. */ minDelay?: number; }; type UseHandledIntervalReturn = { isRunning: boolean; start: () => void; pause: () => void; reset: () => void; setDelay: (newDelay: number) => void; }; /** * useHandledInterval * * @description An enhanced interval hook that provides control methods such as pause, resume, and reset. * Additionally, it can run on a random delay between 0 and the specified `delay` if `random` is enabled. * * @param {UseHandledIntervalOptions} options - An object containing: * - `callback`: The function to execute at each interval tick. * - `delay`: The base interval (ms). * - `random`: (optional) If true, each interval is randomly calculated in the range [0..delay]. * - `autoStart`: (optional) If true, starts the interval automatically. Default is true. * - `minDelay`: (optional) Minimum delay when using random intervals. Default is 0. * @returns {UseHandledIntervalReturn} An object containing control methods and state: * - `isRunning`: Current running state * - `start`: Start/resume the interval * - `pause`: Pause the interval * - `reset`: Reset and restart the interval * - `setDelay`: Update the interval delay * * @returns {{isRunning: boolean, start: () => void, pause: () => void, reset: () => void}} * An object providing: * - `isRunning`: Indicates whether the interval is currently running. * - `start`: A function to start or resume the interval. * - `pause`: Pauses the interval. * - `reset`: Pauses and restarts the interval from scratch. * * @example * ```ts * const { isRunning, start, pause, reset } = useHandledInterval({ * callback: () => console.log('Tick'), * delay: 1000, * random: false * }); * ``` */ declare function useHandledInterval({ callback, delay, random, autoStart, minDelay }: UseHandledIntervalOptions): UseHandledIntervalReturn; type UseIdlenessOptions = { /** Time in milliseconds after which the user is considered idle. */ timeout: number; /** A list of browser events that reset the idle timer. */ events?: string[]; /** Whether to start monitoring immediately. Default is true. */ startImmediately?: boolean; /** Callback function to execute when idle state changes. */ onIdleChange?: (isIdle: boolean) => void; }; type UseIdlenessReturn = { /** Current idle state */ isIdle: boolean; /** Start monitoring for idleness */ start: () => void; /** Stop monitoring for idleness */ stop: () => void; /** Reset the idle timer */ reset: () => void; }; /** * useIdleness * * @description This hook detects whether the user is idle after a specified timeout period. * By default, it listens to user activity events such as `mousemove`, `keydown`, `wheel`, and `touchstart`. * Once the user is inactive for the given `timeout`, the hook returns `true`. If any of the specified events occur, * the timer resets and the hook returns `false` again. * * @param {UseIdlenessOptions} options - Object containing: * - `timeout`: The idle threshold in milliseconds. * - `events`: (optional) Array of event names that should reset the idle timer. * - `startImmediately`: (optional) Whether to start monitoring immediately. * - `onIdleChange`: (optional) Callback for idle state changes. * * @returns {boolean} - A boolean indicating whether the user is currently idle (`true`) or active (`false`). * * @example * ```ts * const isIdle = useIdleness({ timeout: 5000 }); * * if (isIdle) { * // The user has been idle for 5 seconds. * } * ``` */ declare function useIdleness({ timeout, events, startImmediately, onIdleChange }: UseIdlenessOptions): UseIdlenessReturn; type UseIntervalOptions = { /** The callback function to be called repeatedly. */ callback: () => void; /** The time in milliseconds between each call. If `null`, the interval is paused. */ delay: number | null; /** Whether to run the callback immediately when starting. Default is false. */ runImmediately?: boolean; /** Whether to start the interval automatically. Default is true. */ autoStart?: boolean; }; type UseIntervalReturn = { /** Whether the interval is currently running */ isRunning: boolean; /** Start or resume the interval */ start: () => void; /** Pause the interval */ pause: () => void; /** Reset and restart the interval */ restart: () => void; }; /** * useInterval * * @description This hook repeatedly executes a callback function at specified intervals. * If `delay` is `null`, the interval will be paused. * * @param {UseIntervalOptions} options - Object containing: * - `callback`: The function to be executed at each interval. * - `delay`: The interval in milliseconds, or `null` to pause. * - `runImmediately`: Whether to run callback immediately when starting. * - `autoStart`: Whether to start interval automatically. * * @example * ```ts * useInterval({ * callback: () => { * console.log('Repeating task every second'); * }, * delay: 1000, * }); * ``` * * @returns {UseIntervalReturn} An object containing: * - `isRunning`: Whether the interval is active * - `start`: Function to start/resume the interval * - `pause`: Function to pause the interval * - `restart`: Function to reset and restart the interval */ declare function useInterval({ callback, delay, runImmediately, autoStart }: UseIntervalOptions): UseIntervalReturn; interface StorageOptions$1<T> { /** Serializer function to convert value to string */ serializer?: (value: T) => string; /** Deserializer function to parse stored string */ deserializer?: (value: string) => T; /** Error handler function */ onError?: (error: Error) => void; /** Whether to sync across browser tabs */ syncTabs?: boolean; } /** * useLocalStorage * * @description A hook that synchronizes a stateful value with the browser's Local Storage. * The hook reads the stored value from Local Storage on initial render, and writes updates back to Local Storage. * * @typeParam T - The type of the value to store. * @param {string} key - The key under which the value is stored in Local Storage. * @param {T} initialValue - The default value if none is found in Local Storage. * @param {StorageOptions<T>} [options] - Optional configuration. * @returns {[T, (value: T | ((val: T) => T)) => void]} A tuple with: * - The current stored value. * - A setter function that updates both the state and Local Storage. * * @example * ```ts * const [username, setUsername] = useLocalStorage('username', ''); * ``` */ declare function useLocalStorage<T>(key: string, initialValue: T, options?: StorageOptions$1<T>): readonly [T, (value: T | ((val: T) => T)) => void]; interface UseLockBodyScrollOptions { /** Whether to preserve current scroll position. Default is true. */ preservePosition?: boolean; /** Whether to lock scroll immediately. Default is true. */ lockImmediately?: boolean; /** Additional CSS to apply when locked. */ additionalStyles?: Partial<CSSStyleDeclaration>; } interface UseLockBodyScrollReturn { /** Whether the body scroll is currently locked */ isLocked: boolean; /** Lock the body scroll */ lock: () => void; /** Unlock the body scroll */ unlock: () => void; /** Toggle the lock state */ toggle: () => void; } /** * useLockBodyScroll * * @description Prevents scrolling of the `document.body` while the component is mounted. * Restores the original overflow setting when the component unmounts. * * @param {UseLockBodyScrollOptions} [options] - Optional configuration. * @example * ```ts * // In a modal component * useLockBodyScroll(); * ``` */ declare function useLockBodyScroll({ preservePosition, lockImmediately, additionalStyles }?: UseLockBodyScrollOptions): UseLockBodyScrollReturn; type LogLevel = 'info' | 'warn' | 'error' | 'debug'; type LoggerOptions = { /** Log level to use. Defaults to 'info'. */ level?: LogLevel; /** Whether to include timestamps in logs. Defaults to true. */ timestamp?: boolean; /** Whether logging is enabled. Defaults to true. */ enabled?: boolean; /** Custom formatter for the log message. */ formatter?: (label: string, value: unknown) => string; }; /** * useLogger * * @description Logs a message to the console whenever the provided `value` changes. * This can be useful for debugging or tracking state changes. * * @param {string} label - A string label to precede the value in the console log. * @param {unknown} value - The value to log whenever it changes. * @param {LoggerOptions} [options] - Optional configuration for the logger. * @returns {() => void} - A function to manually trigger logging. * * @example * ```ts * useLogger('Current count', count); * ``` */ declare function useLogger(label: string, value: unknown, options?: LoggerOptions): () => void; interface Size { width: number; height: number; } /** * useMeasure * * @description Measures the width and height of a referenced HTML element using the `ResizeObserver` API. * Returns a `ref` callback to be assigned to the element, and a `size` object containing the dimensions. * * @returns {{ * ref: (node: HTMLDivElement | null) => void, * size: { width: number; height: number; } * }} - An object containing: * - `ref`: A callback ref function to attach to the element you want to measure. * - `size`: An object with `width` and `height` of the element. * * @example * ```ts * const { ref, size } = useMeasure(); * return <div ref={ref}>Width: {size.width}, Height: {size.height}</div>; * ``` */ declare function useMeasure(): { ref: (node: HTMLDivElement | null) => (() => void) | undefined; size: Size; }; interface UseMediaQueryOptions { /** Whether to initialize with SSR-safe default. Default is true. */ ssrSafe?: boolean; /** Default value when SSR safe mode is enabled. Default is false. */ ssrDefaultValue?: boolean; /** Whether to start watching immediately. Default is true. */ watchImmediately?: boolean; } interface UseMediaQueryReturn { /** Whether the media query matches */ matches: boolean; /** Start watching for media query changes */ startWatching: () => void; /** Stop watching for media query changes */ stopWatching: () => void; } /** * useMediaQuery * * @description Evaluates a CSS media query and returns a boolean indicating whether it currently matches. * This hook automatically re-evaluates when the window is resized or the media query status changes. * * @param {string} query - A valid CSS media query string, such as `(max-width: 768px)`. * @param {UseMediaQueryOptions} [options] - Optional configuration. * @returns {UseMediaQueryReturn} - An object containing `matches` (boolean), `startWatching` (function), and `stopWatching` (function). * * @example * ```ts * const { matches: isMobile } = useMediaQuery('(max-width: 768px)'); * console.log(isMobile ? 'Mobile layout' : 'Desktop layout'); * * const { matches: isTablet, startWatching, stopWatching } = useMediaQuery('(min-width: 769px) and (max-width: 1024px)', { watchImmediately: false }); * // Later... * startWatching(); * // Later... * stopWatching(); * ``` */ declare function useMediaQuery(query: string, { ssrSafe, ssrDefaultValue, watchImmediately }?: UseMediaQueryOptions): UseMediaQueryReturn; interface NetworkState { online: boolean; downlink?: number; downlinkMax?: number; effectiveType?: 'slow-2g' | '2g' | '3g' | '4g'; rtt?: number; saveData?: boolean; type?: 'bluetooth' | 'cellular' | 'ethernet' | 'none' | 'wifi' | 'wimax' | 'other' | 'unknown'; } interface UseNetworkStateReturn extends NetworkState { checkConnection: () => void; } /** * useNetworkState * * @description Tracks the user's network connectivity (online/offline). Returns `true` if the user is online, `false` if offline. * Under the hood, it listens to the browser's `online` and `offline` events. * * @returns {UseNetworkStateReturn} - Network state and control methods. * @returns {boolean} - A boolean indicating whether the user is currently online (`true`) or offline (`false`). * * @example * ```ts * const isOnline = useNetworkState(); * console.log(isOnline ? 'Connected' : 'Disconnected'); * ``` */ declare function useNetworkState(): UseNetworkStateReturn; interface UsePageLeaveOptions { /** Threshold in pixels from the top of the page. Default is 0. */ threshold?: number; /** Whether to track mouse position. Default is true. */ enabled?: boolean; /** Callback when user leaves the page */ onLeave?: () => void; /** Callback when user returns to the page */ onReturn?: () => void; } interface UsePageLeaveReturn { /** Whether the mouse has left the page */ hasLeft: boolean; /** Enable tracking */ enable: () => void; /** Disable tracking */ disable: () => void; } /** * usePageLeave * * @description This hook detects when the user moves their mouse out of the top edge of the browser window, * typically indicating an intent to leave the page. It returns a boolean: `true` if the mouse has left, * `false` otherwise. * @param {UsePageLeaveOptions} [options] - Optional configuration. * * @returns {boolean} - `true` when the mouse pointer moves out of the top boundary of the window. * * @example * ```ts * const { hasLeft } = usePageLeave(); * if (hasLeft) { *   // Show a modal or an exit-intent popup. * } * * // With options: * // const { hasLeft, enable, disable } = usePageLeave({ threshold: 20, enabled: false, onLeave: () => console.log('Left!'), onReturn: () => console.log('Returned!') }); * ``` */ declare function usePageLeave(options?: UsePageLeaveOptions): UsePageLeaveReturn; interface QueueOperations<T> { /** Add an item to the end of the queue */ enqueue: (item: T) => void; /** Remove and return the first item from the queue */ dequeue: () => T | undefined; /** Clear all items from the queue */ clear: () => void; /** Get the first item without removing it */ peek: () => T | undefined; /** Get the last item without removing it */ peekLast: () => T | undefined; /** Check if the queue is empty */ isEmpty: boolean; /** Get the current size of the queue */ size: number; /** Get all items as an array */ toArray: () => T[]; /** Check if an item exists in the queue */ contains: (item: T) => boolean; } interface UseQueueOptions<T> { /** Maximum size of the queue. Default is unlimited. */ maxSize?: number; /** Callback when queue is full */ onFull?: () => void; /** Callback when queue becomes empty */ onEmpty?: () => void; /** Custom equality function for contains() */ equalityFn?: (a: T, b: T) => boolean; } /** * useQueue * * @description A custom hook that manages a FIFO (first-in-first-out) queue. * It provides methods to add items, remove items, reset the queue, and retrieve the first/last element. * * @param {T[]} initialValues - Initial items in the queue. * @param {UseQueueOptions<T>} [options] - Optional configuration. * @typeParam T - The type of items in the queue. * @returns {QueueOperations<T>} - An object containing queue operations and state. * * @example * ```ts * const { enqueue, dequeue, size, peek, toArray } = useQueue<number>([1, 2]); * enqueue(3); * console.log(toArray()); // [1, 2, 3] * const first = dequeue(); // first is 1, queue becomes [2, 3] * console.log(peek()); // 2 * ``` */ declare function useQueue<T>(initialValues?: T[], options?: UseQueueOptions<T>): QueueOperations<T>; interface StorageOptions<T> { /** Serializer function to convert value to string */ serializer?: (value: T) => string; /** Deserializer function to parse stored string */ deserializer?: (value: string) => T; /** Error handler function */ onError?: (error: Error) => void; /** Whether to sync across browser tabs */ syncTabs?: boolean; } /** * useSessionStorage * * @description A hook that synchronizes a stateful value with the browser's Session Storage. * Similar to `useLocalStorage`, but it uses `sessionStorage` under the hood. * Data stored in Session Storage is cleared after the session ends (e.g., when the browser tab is closed). * * @typeParam T - The type of the value to store. * @param {string} key - The key under which the value is stored in Session Storage. * @param {T} initialValue - The default value if none is found in Session Storage. * @param {StorageOptions<T>} [options] - Optional configuration. * @returns {[T, (value: T | ((val: T) => T)) => void]} A tuple with: * - The current stored value. * - A setter function that updates both the state and Session Storage. * * @example * ```ts * const [sessionData, setSessionData] = useSessionStorage('session-key', { foo: 'bar' }); * ``` */ declare function useSessionStorage<T>(key: string, initialValue: T, options?: StorageOptions<T>): readonly [T, (value: T | ((val: T) => T)) => void]; type UseTimeoutOptions = { /** The callback function to run after the specified delay. */ callback: () => void; /** The time in milliseconds to wait before calling `callback`. If `null`, the timeout is not set. */ delay: number | null; /** Whether to start the timeout automatically. Default is true. */ autoStart?: boolean; }; type UseTimeoutReturn = { /** Whether the timeout is currently pending */ isPending: boolean; /** Start or restart the timeout */ start: () => void; /** Cancel the timeout */ cancel: () => void; /** Reset and restart the timeout */ reset: () => void; }; /** * useTimeout * * @description This hook executes a callback function once after a specified delay. If the `delay` is `null`, * it will not schedule any timeout. Changing the `delay` or `callback` will reset the timeout. * * @returns {UseTimeoutReturn} Control methods and state for the timeout. * @param {UseTimeoutOptions} options - Object containing: *  - `callback`: The function to be executed. *  - `delay`: The time in milliseconds after which `callback` is called, or `null` to disable the timeout. *  - `autoStart`: Whether to start the timeout automatically on mount or when dependencies change. Default is true. * * @example * ```ts * const { isPending, start, cancel, reset } = useTimeout({ *   callback: () => { *     console.log('Hello after 3 seconds'); *   }, *   delay: 3000, * }); * * // To manually start/stop: * // const { isPending, start, cancel, reset } = useTimeout({ callback: () => console.log('Manual'), delay: 1000, autoStart: false }); * // useEffect(() => { start(); }, []); // Start manually after initial render * ``` */ declare function useTimeout({ callback, delay, autoStart }: UseTimeoutOptions): UseTimeoutReturn; interface UseToggleOptions { /** Initial value for the toggle */ initialValue?: boolean; /** Callback when value changes */ onChange?: (value: boolean) => void; /** Whether to persist the value in localStorage */ persist?: boolean; /** Key to use for localStorage persistence */ storageKey?: string; } interface UseToggleReturn { /** Current value */ value: boolean; /** Toggle the value */ toggle: () => void; /** Set value to true */ setTrue: () => void; /** Set value to false */ setFalse: () => void; /** Set value directly */ setValue: (value: boolean) => void; } /** * useToggle * * @description Manages a boolean state with a toggle function. Provides an easy way * to switch a value between `true` and `false`. * * @param {UseToggleOptions} [options] - Optional configuration. * @param {boolean} [initialValue=false] - Optional initial value of the toggle. * @returns {[boolean, () => void]} - A tuple containing: * - The current boolean value. * - A function to toggle the value. * * @example * ```ts * const [isOn, toggleIsOn] = useToggle(false); * // isOn === false initially * toggleIsOn(); // isOn === true * ``` */ declare function useToggle({ initialValue, onChange, persist, storageKey }?: UseToggleOptions): UseToggleReturn; interface UseVisibilityChangeOptions { /** Callback when document becomes visible */ onVisible?: () => void; /** Callback when document becomes hidden */ onHidden?: () => void; /** Whether to start monitoring immediately */ startImmediately?: boolean; } interface UseVisibilityChangeReturn { /** Whether the document is currently visible */ isVisible: boolean; /** Start monitoring visibility changes */ start: () => void; /** Stop monitoring visibility changes */ stop: () => void; } /** * useVisibilityChange * * @description Tracks the visibility state of the current document (i.e., whether the tab is in the foreground). * Returns `true` if the document is visible, or `false` if it's hidden. * Provides methods to start and stop monitoring. * * @param {UseVisibilityChangeOptions} [options] - Optional configuration. * @returns {UseVisibilityChangeReturn} - An object containing the current visibility state and control methods. * * @example * ```ts * const { isVisible, start, stop } = useVisibilityChange({ startImmediately: true }); * * useEffect(() => { *   console.log(isVisible ? 'Tab is active' : 'Tab is hidden'); * }, [isVisible]); * * // To manually control monitoring: * // const { isVisible, start, stop } = useVisibilityChange({ startImmediately: false }); * // // ... later ... * // start(); * ``` */ declare function useVisibilityChange({ onVisible, onHidden, startImmediately }?: UseVisibilityChangeOptions): UseVisibilityChangeReturn; interface UseWindowWidthOptions { /** Debounce delay in milliseconds */ debounceDelay?: number; /** Whether to start monitoring immediately */ startImmediately?: boolean; /** Initial width to use (defaults to window.innerWidth) */ initialWidth?: number; /** Callback when width changes */ onChange?: (width: number) => void; } interface UseWindowWidthReturn { /** Current window width */ width: number; /** Start monitoring width changes */ start: () => void; /** Stop monitoring width changes */ stop: () => void; } /** * useWindowWidth * * @description A hook that gets the current window width and * updates it automatically on resize with debouncing. * Provides methods to start and stop monitoring. * * @param {UseWindowWidthOptions} [options] - Optional configuration. * @returns {UseWindowWidthReturn} - An object containing the current width and control methods. * * @example * // Example of use in a functional component: * import React from 'react'; * import { useWindowWidth } from 'my-react-hooks'; * * function Demo() { *   const { width, start, stop } = useWindowWidth({ debounceDelay: 100 }); *   return <div>El ancho de la ventana es: {width}px</div>; // Keep Spanish in example string * } */ declare function useWindowWidth({ debounceDelay, startImmediately, initialWidth, onChange }?: UseWindowWidthOptions): UseWindowWidthReturn; export { UsePageLeaveOptions, UsePageLeaveReturn, useBoolean, useClickOutside, useCopyToClipboard, useDebounce, useHandledInterval, useIdleness, useInterval, useLocalStorage, useLockBodyScroll, useLogger, useMeasure, useMediaQuery, useNetworkState, usePageLeave, useQueue, useSessionStorage, useTimeout, useToggle, useVisibilityChange, useWindowWidth };