UNPKG

@thibault.sh/hooks

Version:

A comprehensive collection of React hooks for browser storage, UI interactions, and more

939 lines (915 loc) 32 kB
import { RefObject } from 'react'; interface AsyncState<T> { isLoading: boolean; error: Error | null; value: T | null; } /** * Hook that manages async operations with loading, error, and success states. * * Provides a clean interface for handling asynchronous functions with automatic * state management for loading indicators and error handling. * * @template T - The type of data returned by the async function * @param asyncFunction - The async function to execute * * @returns An object containing: * - `execute`: Function to trigger the async operation * - `status`: Current state with `isLoading`, `error`, and `value` properties * * @example * ```tsx * const fetchUser = async (id: string) => { * const response = await fetch(`/api/users/${id}`); * return response.json(); * }; * * const { execute, status } = useAsync(fetchUser); * * // In your component * if (status.isLoading) return <div>Loading...</div>; * if (status.error) return <div>Error: {status.error.message}</div>; * if (status.value) return <div>User: {status.value.name}</div>; * * // Trigger the async operation * <button onClick={() => execute('user-123')}>Load User</button> * ``` * * @see https://thibault.sh/hooks/use-async */ declare function useAsync<T>(asyncFunction: (...args: any[]) => Promise<T>): { execute: (...args: any[]) => Promise<void>; status: AsyncState<T>; }; type Handler = (event: MouseEvent | TouchEvent) => void; /** * Hook that detects clicks outside a referenced element and executes a callback. * * Useful for implementing dropdown menus, modals, or any component that should * close when the user clicks outside of it. Handles both mouse and touch events. * * @template T - The type of HTML element being referenced * @param ref - React ref object pointing to the element to monitor * @param handler - Callback function executed when a click occurs outside the element * * @example * ```tsx * function Dropdown() { * const [isOpen, setIsOpen] = useState(false); * const dropdownRef = useRef<HTMLDivElement>(null); * * useClickOutside(dropdownRef, () => { * setIsOpen(false); * }); * * return ( * <div ref={dropdownRef}> * <button onClick={() => setIsOpen(!isOpen)}> * Toggle Dropdown * </button> * {isOpen && ( * <div className="dropdown-menu"> * <p>Dropdown content</p> * </div> * )} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-click-outside */ declare const useClickOutside: <T extends HTMLElement = HTMLElement>(ref: RefObject<T | null> | null, handler: Handler) => void; interface ContainerScroll { scrollTop: number; scrollLeft: number; scrollWidth: number; scrollHeight: number; clientWidth: number; clientHeight: number; isScrolling: boolean; } /** * Hook that tracks scroll position, dimensions, and scrolling state of a container element. * * Provides real-time scroll information including position, dimensions, and a debounced * scrolling indicator that's useful for optimizing scroll-based animations or effects. * * @param containerRef - React ref object pointing to the scrollable container element * @param delay - Delay in milliseconds before setting `isScrolling` to false (default: 150) * * @returns An object containing: * - `scrollTop`: Vertical scroll position in pixels * - `scrollLeft`: Horizontal scroll position in pixels * - `scrollWidth`: Total scrollable width of the content * - `scrollHeight`: Total scrollable height of the content * - `clientWidth`: Visible width of the container * - `clientHeight`: Visible height of the container * - `isScrolling`: Boolean indicating if the user is currently scrolling (debounced) * * @example * ```tsx * const containerRef = useRef<HTMLDivElement>(null); * const scroll = useContainerScroll(containerRef, 200); * * return ( * <div ref={containerRef} className="scrollable-container"> * <div>Scroll position: {scroll.scrollTop}px</div> * <div>Container size: {scroll.clientWidth}x{scroll.clientHeight}</div> * <div>Content size: {scroll.scrollWidth}x{scroll.scrollHeight}</div> * {scroll.isScrolling && <div>Currently scrolling...</div>} * * <div style={{ height: '2000px' }}>Long content...</div> * </div> * ); * ``` * @see https://thibault.sh/hooks/use-container-scroll */ declare function useContainerScroll(containerRef: RefObject<HTMLElement | null>, delay?: number): ContainerScroll; /** * Options for configuring cookie behavior */ interface CookieOptions { /** Number of days until the cookie expires (default: 7) */ days?: number; /** Cookie path (default: "/") */ path?: string; /** Cookie domain */ domain?: string; /** Whether the cookie requires HTTPS (default: false) */ secure?: boolean; /** SameSite cookie attribute (default: "Lax") */ sameSite?: "Strict" | "Lax" | "None"; } /** * Hook for managing state that persists in browser cookies with SSR support. * * Provides a React state-like interface for reading, writing, and deleting cookies. * Automatically handles encoding/decoding, error handling, and server-side rendering compatibility. * * @param name - The name of the cookie to manage * @param initialValue - The default value to use when no cookie exists or on server-side * * @returns A tuple containing: * - `value`: Current cookie value as string, or null if not set * - `setCookie`: Function to update the cookie with optional configuration * - `deleteCookie`: Function to remove the cookie from the browser * * @example * ```tsx * const [theme, setTheme, deleteTheme] = useCookieState('theme', 'light'); * * // Read current value * console.log(theme); // 'light' or saved value * * // Update cookie with default options (7 days expiry) * setTheme('dark'); * * // Update with custom options * setTheme('dark', { * days: 30, * secure: true, * sameSite: 'Strict' * }); * * // Remove the cookie * deleteTheme(); * ``` * * @example * ```tsx * // User preferences with longer expiry * const [userPrefs, setUserPrefs] = useCookieState('preferences', '{}'); * * const updatePreference = (key: string, value: any) => { * const prefs = JSON.parse(userPrefs || '{}'); * prefs[key] = value; * setUserPrefs(JSON.stringify(prefs), { days: 365 }); * }; * ``` * @see https://thibault.sh/hooks/use-cookie-state */ declare function useCookieState(name: string, initialValue: string): [string | null, (newValue: string, options?: CookieOptions) => void, () => void]; /** * Hook that creates a countdown timer to a target date with automatic updates. * * Provides real-time countdown values that update at a specified interval. * Returns zero values when the target date has passed. * * @param countDownDate - Target date as a timestamp in milliseconds (e.g., `new Date('2024-12-31').getTime()`) * @param refreshRate - Update interval in milliseconds (defaults to 1000ms for 1-second updates) * * @returns A readonly tuple `[days, hours, minutes, seconds]` representing time remaining * * @example * ```tsx * const targetDate = new Date('2024-12-31 23:59:59').getTime(); * const [days, hours, minutes, seconds] = useCountdown(targetDate); * * return ( * <div> * {days}d {hours}h {minutes}m {seconds}s remaining * </div> * ); * ``` * * @example * // Custom refresh rate (every 100ms for smoother animation) * const [days, hours, minutes, seconds] = useCountdown(targetDate, 100); * @see https://thibault.sh/hooks/use-countdown */ declare function useCountdown(countDownDate: number, refreshRate?: number): number[] | readonly [number, number, number, number]; /** * Hook that tracks the state of a CSS media query and updates when it changes. * * Provides a reactive way to respond to viewport changes, screen sizes, or any * CSS media query conditions in your React components. * * @param query - The CSS media query string to track (e.g., "(min-width: 768px)") * * @returns Boolean indicating whether the media query currently matches * * @example * ```tsx * function ResponsiveComponent() { * const isMobile = useMediaQuery('(max-width: 768px)'); * const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)'); * const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); * * return ( * <div> * {isMobile && <MobileLayout />} * {isTablet && <TabletLayout />} * {isDarkMode ? <DarkTheme /> : <LightTheme />} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-media-query */ declare const useMediaQuery: (query: string) => boolean; /** * Hook that detects when an element is being hovered over. * * Provides a simple way to track hover state for any HTML element, with support * for both internal ref management and external ref usage. * * @template T - The type of HTML element being monitored (extends HTMLElement) * @param _ref - Optional React ref object for the element to monitor. If not provided, * the hook will create and return its own ref. * * @returns A tuple containing: * - `ref`: React ref object to attach to the target element * - `isHovered`: Boolean indicating whether the element is currently being hovered * * @example * ```tsx * // Using the hook's internal ref * function HoverButton() { * const [ref, isHovered] = useHover<HTMLButtonElement>(); * * return ( * <button * ref={ref} * style={{ backgroundColor: isHovered ? 'lightblue' : 'white' }} * > * {isHovered ? 'Hovered!' : 'Hover me'} * </button> * ); * } * * // Using an external ref * function HoverDiv() { * const myRef = useRef<HTMLDivElement>(null); * const [, isHovered] = useHover(myRef); * * return ( * <div ref={myRef}> * {isHovered && <span>You're hovering!</span>} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-hover */ declare const useHover: <T extends HTMLElement>(_ref?: RefObject<T | null> | null) => [RefObject<T>, boolean]; /** * Hook that detects when a specific key is pressed and held down. * * Tracks the current pressed state of a keyboard key, returning true while * the key is held down and false when released. * * @param targetKey - The key to detect (e.g., "Enter", "Escape", "ArrowUp", "a") * * @returns Boolean indicating if the target key is currently pressed * * @example * ```tsx * function GameControls() { * const isSpacePressed = useKeyPress(' '); * const isEnterPressed = useKeyPress('Enter'); * const isArrowUpPressed = useKeyPress('ArrowUp'); * * return ( * <div> * <p>Space: {isSpacePressed ? 'Pressed' : 'Released'}</p> * <p>Enter: {isEnterPressed ? 'Pressed' : 'Released'}</p> * <p>Arrow Up: {isArrowUpPressed ? 'Pressed' : 'Released'}</p> * * {isSpacePressed && <div>🚀 Boost active!</div>} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-key-press */ declare function useKeyPress(targetKey: string): boolean; type KeyCombo = string[]; /** * Hook that detects when a specific combination of keys is pressed simultaneously. * * Useful for implementing keyboard shortcuts, hotkeys, or complex key combinations * in your React components. The hook tracks all currently pressed keys and returns * true when all keys in the target combination are active. * * @param targetCombo - Array of key names that make up the combination * * @returns Boolean indicating if all keys in the combination are currently pressed * * @example * ```tsx * function App() { * const isSaveCombo = useKeyCombo(['Control', 's']); * const isUndoCombo = useKeyCombo(['Control', 'z']); * const isComplexCombo = useKeyCombo(['Control', 'Shift', 'p']); * * useEffect(() => { * if (isSaveCombo) { * console.log('Save shortcut pressed!'); * // Handle save action * } * }, [isSaveCombo]); * * return ( * <div> * <p>Press Ctrl+S to save</p> * <p>Save combo active: {isSaveCombo ? 'Yes' : 'No'}</p> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-key-combo */ declare function useKeyCombo(targetCombo: KeyCombo): boolean; /** * Configuration options for the useLongPress hook */ interface LongPressOptions { /** Duration in milliseconds before long press is triggered (default: 400) */ delay?: number; /** Whether to disable context menu on long press (default: true) */ preventContext?: boolean; /** Callback fired when a normal press (shorter than delay) is completed */ onPress?: () => void; /** Callback fired when a long press is successfully triggered */ onLongPress?: () => void; /** Callback fired when a long press is canceled before completion */ onLongPressCanceled?: () => void; } /** * Hook that handles both normal press and long press interactions with progress tracking. * * Provides event handlers for detecting short taps vs long presses, with smooth progress * animation and customizable timing. Works with both mouse and touch events. * * @param options - Configuration options for the long press behavior * @param options.delay - Duration in milliseconds before triggering long press (default: 400) * @param options.preventContext - Whether to prevent context menu on long press (default: true) * @param options.onPress - Callback for normal press (when released before delay) * @param options.onLongPress - Callback for successful long press (when delay is reached) * @param options.onLongPressCanceled - Callback when long press is interrupted * * @returns Object containing: * - `handlers`: Event handlers to spread on your element * - `state`: Current press state with `isPressed`, `isLongPressed`, and `progress` (0-1) * * @example * ```tsx * function DeleteButton({ onDelete }) { * const { handlers, state } = useLongPress({ * delay: 1000, * onPress: () => console.log('Quick tap - no action'), * onLongPress: onDelete, * onLongPressCanceled: () => console.log('Canceled deletion') * }); * * return ( * <button {...handlers} className={state.isPressed ? 'pressing' : ''}> * {state.isLongPressed * ? 'Deleting...' * : `Hold to delete (${Math.round(state.progress * 100)}%)` * } * </button> * ); * } * ``` * * @see https://thibault.sh/hooks/use-long-press */ declare function useLongPress(options?: LongPressOptions): { /** Event handlers to attach to the target element */ handlers: { onMouseDown: (event: React.TouchEvent | React.MouseEvent) => void; onMouseUp: (event: React.TouchEvent | React.MouseEvent) => void; onMouseLeave: (event: React.TouchEvent | React.MouseEvent) => void; onTouchStart: (event: React.TouchEvent | React.MouseEvent) => void; onTouchEnd: (event: React.TouchEvent | React.MouseEvent) => void; onTouchCancel: (event: React.TouchEvent | React.MouseEvent) => void; }; /** Current state of the press interaction */ state: { isPressed: boolean; isLongPressed: boolean; progress: number; }; }; interface WindowSize { width: number; height: number; } /** * Hook that tracks the browser window dimensions and updates on resize. * * Automatically listens for window resize events and provides the current * width and height. Safe for SSR environments by checking for window availability. * * @returns An object containing: * - `width`: Current window inner width in pixels * - `height`: Current window inner height in pixels * * @example * ```tsx * function ResponsiveComponent() { * const { width, height } = useWindowSize(); * * return ( * <div> * <p>Window size: {width} x {height}</p> * {width < 768 ? ( * <MobileLayout /> * ) : ( * <DesktopLayout /> * )} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-window-size */ declare function useWindowSize(): WindowSize; interface ScrollPosition { x: number; y: number; } /** * Hook that tracks the current window scroll position in real-time. * * Automatically updates when the user scrolls and handles SSR scenarios * by safely checking for window availability. * * @returns An object containing: * - `x`: Horizontal scroll position in pixels * - `y`: Vertical scroll position in pixels * * @example * ```tsx * function ScrollIndicator() { * const { x, y } = useScrollPosition(); * * return ( * <div className="scroll-info"> * <p>Horizontal: {x}px</p> * <p>Vertical: {y}px</p> * {y > 100 && ( * <button onClick={() => window.scrollTo(0, 0)}> * Back to Top * </button> * )} * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-scroll-position */ declare function useScrollPosition(): ScrollPosition; interface ElementSize { width: number; height: number; } /** * Hook that tracks an element's dimensions and updates when the element is resized. * * Uses ResizeObserver to efficiently monitor size changes and provides real-time * width and height measurements. Automatically handles cleanup and provides * initial measurements immediately. * * @param elementRef - React ref object pointing to the target HTML element * * @returns An object containing: * - `width`: Current width of the element in pixels * - `height`: Current height of the element in pixels * * @example * ```tsx * function ResizableComponent() { * const elementRef = useRef<HTMLDivElement>(null); * const { width, height } = useElementSize(elementRef); * * return ( * <div> * <div ref={elementRef} style={{ resize: 'both', overflow: 'auto' }}> * Resizable content * </div> * <p>Size: {width} x {height}px</p> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-element-size */ declare function useElementSize<T extends HTMLElement>(elementRef: RefObject<T | null> | null): ElementSize; interface IntersectionOptions extends IntersectionObserverInit { freezeOnceVisible?: boolean; } /** * Hook that tracks when an element enters or leaves the viewport using the Intersection Observer API. * * Useful for implementing lazy loading, infinite scrolling, animations on scroll, * or tracking visibility of elements for analytics purposes. * * @param elementRef - React ref object pointing to the target element to observe * @param options - Configuration options for the intersection observer: * - `threshold`: Number or array defining at what percentage of visibility the callback should trigger (0-1) * - `root`: Element used as viewport for checking visibility (defaults to browser viewport) * - `rootMargin`: Margin around the root element (CSS-like syntax, e.g., "10px 20px") * - `freezeOnceVisible`: If true, stops observing once element becomes visible (useful for one-time animations) * * @returns IntersectionObserverEntry object containing visibility information, or null if element not found * * @example * ```tsx * function LazyImage({ src, alt }: { src: string; alt: string }) { * const imgRef = useRef<HTMLImageElement>(null); * const entry = useIntersectionObserver(imgRef, { * threshold: 0.1, * freezeOnceVisible: true * }); * * const isVisible = entry?.isIntersecting; * * return ( * <img * ref={imgRef} * src={isVisible ? src : undefined} * alt={alt} * style={{ opacity: isVisible ? 1 : 0 }} * /> * ); * } * ``` * * @see https://thibault.sh/hooks/use-intersection-observer */ declare function useIntersectionObserver<T extends HTMLElement>(elementRef: RefObject<T | null> | null, { threshold, root, rootMargin, freezeOnceVisible }?: IntersectionOptions): IntersectionObserverEntry | null; interface ResizeObserverEntry { contentRect: DOMRectReadOnly; contentBoxSize: ReadonlyArray<ResizeObserverSize>; borderBoxSize: ReadonlyArray<ResizeObserverSize>; devicePixelContentBoxSize: ReadonlyArray<ResizeObserverSize>; target: Element; } /** * Hook that observes element size changes using the ResizeObserver API. * * Provides detailed resize information including content rect, box sizes, and device pixel ratios. * Automatically cleans up the observer when the component unmounts or the ref changes. * * @param elementRef - React ref object pointing to the target element to observe * * @returns ResizeObserverEntry with detailed size information, or null if no element or no resize has occurred * - `contentRect`: The content rectangle of the element * - `contentBoxSize`: Array of content box dimensions * - `borderBoxSize`: Array of border box dimensions * - `devicePixelContentBoxSize`: Array of device pixel content box dimensions * - `target`: The observed element * * @example * ```tsx * function ResponsiveComponent() { * const containerRef = useRef<HTMLDivElement>(null); * const resizeEntry = useResizeObserver(containerRef); * * const width = resizeEntry?.contentRect.width ?? 0; * const height = resizeEntry?.contentRect.height ?? 0; * * return ( * <div ref={containerRef}> * <p>Size: {width} x {height}</p> * <div>Content adapts based on container size</div> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-resize-observer */ declare function useResizeObserver<T extends HTMLElement>(elementRef: RefObject<T | null> | null): ResizeObserverEntry | null; /** * Hook that debounces a value, delaying updates until after the specified delay. * * Useful for optimizing performance in scenarios like search inputs, API calls, * or any situation where you want to limit how often a value changes. * * @template T - The type of the value being debounced * @param value - The value to debounce * @param delay - The delay in milliseconds before the value updates * * @returns The debounced value that only updates after the delay period * * @example * ```tsx * function SearchInput() { * const [searchTerm, setSearchTerm] = useState(''); * const debouncedSearchTerm = useDebounce(searchTerm, 300); * * useEffect(() => { * if (debouncedSearchTerm) { * // This will only run 300ms after the user stops typing * searchAPI(debouncedSearchTerm); * } * }, [debouncedSearchTerm]); * * return ( * <input * value={searchTerm} * onChange={(e) => setSearchTerm(e.target.value)} * placeholder="Search..." * /> * ); * } * ``` * @see https://thibault.sh/hooks/use-debounce */ declare function useDebounce<T>(value: T, delay: number): T; /** * Hook that throttles a value, limiting how often it can update. * * Unlike debouncing which delays execution until after a quiet period, throttling * ensures the value updates at most once per specified interval, making it ideal * for scroll events, resize handlers, or any high-frequency updates. * * @template T - The type of the value being throttled * @param value - The value to throttle * @param interval - The minimum time interval between updates in milliseconds * * @returns The throttled value that updates at most once per interval * * @example * ```tsx * function ScrollTracker() { * const [scrollY, setScrollY] = useState(0); * const throttledScrollY = useThrottle(scrollY, 100); * * useEffect(() => { * const handleScroll = () => setScrollY(window.scrollY); * window.addEventListener('scroll', handleScroll); * return () => window.removeEventListener('scroll', handleScroll); * }, []); * * useEffect(() => { * // This will only run at most once every 100ms * console.log('Throttled scroll position:', throttledScrollY); * }, [throttledScrollY]); * * return <div>Scroll position: {throttledScrollY}px</div>; * } * ``` * * @see https://thibault.sh/hooks/use-throttle */ declare function useThrottle<T>(value: T, interval: number): T; /** * Hook that creates a setInterval that automatically cleans up on unmount. * * Provides a declarative way to use intervals in React components with proper * cleanup and the ability to pause/resume by passing null as the delay. * * @param callback - The function to execute on each interval tick * @param delay - The delay in milliseconds between executions, or null to pause the interval * * @example * ```tsx * function Timer() { * const [count, setCount] = useState(0); * const [isRunning, setIsRunning] = useState(true); * * // Increment count every second when running * useInterval(() => { * setCount(count => count + 1); * }, isRunning ? 1000 : null); * * return ( * <div> * <p>Count: {count}</p> * <button onClick={() => setIsRunning(!isRunning)}> * {isRunning ? 'Pause' : 'Resume'} * </button> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-interval */ declare function useInterval(callback: () => void, delay: number | null): void; type EventMap = WindowEventMap & HTMLElementEventMap & DocumentEventMap; /** * Hook that adds an event listener to a target element with automatic cleanup. * * Automatically handles adding and removing event listeners, ensuring proper cleanup * when the component unmounts or dependencies change. Supports all standard DOM events * on window, document, or specific HTML elements. * * @template K - The event type key from the event map * @param eventName - The name of the event to listen for (e.g., 'click', 'keydown', 'resize') * @param handler - The event handler function that will be called when the event fires * @param element - Optional ref to the target element. Defaults to window if not provided * @param options - Optional event listener options (capture, once, passive, etc.) * * @example * ```tsx * function Component() { * const buttonRef = useRef<HTMLButtonElement>(null); * * // Listen for clicks on a specific element * useEventListener('click', (e) => { * console.log('Button clicked!', e); * }, buttonRef);r * * // Listen for window resize events * useEventListener('resize', () => { * console.log('Window resized'); * }); * * // Listen for escape key presses * useEventListener('keydown', (e) => { * if (e.key === 'Escape') { * console.log('Escape pressed'); * } * }); * * return <button ref={buttonRef}>Click me</button>; * } * ``` * * @see https://thibault.sh/hooks/use-event-listener */ declare function useEventListener<K extends keyof EventMap, T extends HTMLElement>(eventName: K, handler: (event: EventMap[K]) => void, element?: RefObject<T | null> | null, options?: boolean | AddEventListenerOptions): void; /** * Hook that manages state synchronized with localStorage. * * Provides a useState-like interface that automatically persists state changes * to localStorage and initializes from stored values on mount. Handles SSR * compatibility and JSON serialization/deserialization automatically. * * @template T - The type of the value being stored * @param key - The localStorage key to use for persistence * @param initialValue - The default value to use if no stored value exists * * @returns A tuple containing: * - Current stored value (T) * - Setter function that updates both state and localStorage * * @example * ```tsx * function UserPreferences() { * const [theme, setTheme] = useLocalStorageState('theme', 'light'); * const [settings, setSettings] = useLocalStorageState('settings', { * notifications: true, * language: 'en' * }); * * return ( * <div> * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> * Current theme: {theme} * </button> * * <button onClick={() => setSettings(prev => ({ * ...prev, * notifications: !prev.notifications * }))}> * Notifications: {settings.notifications ? 'On' : 'Off'} * </button> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-local-storage-state */ declare function useLocalStorageState<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void]; /** * Hook that manages state synchronized with sessionStorage. * * Provides persistent state that survives page refreshes but is cleared when * the browser tab is closed. Automatically handles JSON serialization/deserialization * and provides SSR-safe initialization. * * @template T - The type of the stored value * @param key - The sessionStorage key to store the value under * @param initialValue - The default value used when no stored value exists * * @returns A tuple containing: * - The current stored value (or initial value if none exists) * - A setter function that updates both state and sessionStorage * * @example * ```tsx * function UserPreferences() { * const [theme, setTheme] = useSessionStorageState('theme', 'light'); * const [sidebarOpen, setSidebarOpen] = useSessionStorageState('sidebar', true); * * return ( * <div> * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> * Current theme: {theme} * </button> * <button onClick={() => setSidebarOpen(!sidebarOpen)}> * Sidebar: {sidebarOpen ? 'Open' : 'Closed'} * </button> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-session-storage-state */ declare function useSessionStorageState<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void]; /** * Hook that manages state synchronized with URL query parameters. * * Automatically persists state to the URL and keeps it in sync with browser * navigation (back/forward buttons). Perfect for shareable URLs and maintaining * state across page refreshes. * * @template T - The type of the state value * @param key - The query parameter key to use in the URL * @param initialValue - Default value when the parameter doesn't exist * @param options - Configuration options for serialization * @param options.serialize - Custom function to convert value to string (defaults to JSON.stringify) * @param options.deserialize - Custom function to parse string back to value (defaults to JSON.parse) * * @returns A tuple containing: * - Current state value (synced with URL) * - Setter function (updates both state and URL) * * @example * ```tsx * function SearchPage() { * const [query, setQuery] = useQueryParamsState('q', ''); * const [filters, setFilters] = useQueryParamsState('filters', { category: 'all' }); * * return ( * <div> * <input * value={query} * onChange={(e) => setQuery(e.target.value)} * placeholder="Search..." * /> * <select * value={filters.category} * onChange={(e) => setFilters({ ...filters, category: e.target.value })} * > * <option value="all">All</option> * <option value="books">Books</option> * </select> * </div> * ); * } * ``` * * @see https://thibault.sh/hooks/use-query-params-state */ declare function useQueryParamsState<T>(key: string, initialValue: T, options?: { serialize?: (value: T) => string; deserialize?: (value: string) => T; }): [T, (value: T | ((val: T) => T)) => void]; export { useAsync, useClickOutside, useContainerScroll, useCookieState, useCountdown, useDebounce, useElementSize, useEventListener, useHover, useIntersectionObserver, useInterval, useKeyCombo, useKeyPress, useLocalStorageState, useLongPress, useMediaQuery, useQueryParamsState, useResizeObserver, useScrollPosition, useSessionStorageState, useThrottle, useWindowSize };