react-essentials-functions 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,524 @@
1
1
  # react-essentials-functions
2
2
 
3
- A collection of useful `hooks` and `components` for React that I use in my projects.
3
+ A collection of zero-dependency useful hooks and components for React.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
8
  yarn add react-essentials-functions
9
+ # or
10
+ npm install react-essentials-functions
9
11
  ```
10
12
 
11
- ## Usage
13
+ ## Table of Contents
12
14
 
13
- Documentation is in progress... For now, you can check the source code of the components and hooks.
15
+ - [Hooks](#hooks)
16
+ - [useClickOutside](#useclickoutside)
17
+ - [useDebounce](#usedebounce)
18
+ - [useDimensions](#usedimensions)
19
+ - [useLocalStorage](#uselocalstorage)
20
+ - [useMediaQuery](#usemediaquery)
21
+ - [usePrevious](#useprevious)
22
+ - [useSafeFetch](#usesafefetch)
23
+ - [useSafeState](#usesafestate)
24
+ - [useScript](#usescript)
25
+ - [useTheme](#usetheme)
26
+ - [useToggle](#usetoggle)
27
+ - [useWindowDimensions](#usewindowdimensions)
28
+ - [Components](#components)
29
+ - [ConditionalWrapper](#conditionalwrapper)
30
+
31
+ ---
32
+
33
+ ## Hooks
34
+
35
+ ### useClickOutside
36
+
37
+ Hook that detects clicks outside of a referenced element. Useful for closing dropdowns, modals, and popovers. Listens for both `mousedown` and `touchstart` events.
38
+
39
+ **Parameters:**
40
+ - `ref: RefObject<HTMLElement>` - React ref to the element to monitor
41
+ - `handler: (event: MouseEvent | TouchEvent) => void` - Callback fired when a click outside is detected
42
+
43
+ **Returns:**
44
+ - `void`
45
+
46
+ **Side effects:**
47
+ - Adds `mousedown` and `touchstart` event listeners on `document`
48
+ - Removes listeners on unmount
49
+
50
+ **Example:**
51
+ ```tsx
52
+ import { useClickOutside } from 'react-essentials-functions';
53
+ import { useRef, useState } from 'react';
54
+
55
+ function Dropdown() {
56
+ const dropdownRef = useRef<HTMLDivElement>(null);
57
+ const [isOpen, setIsOpen] = useState(false);
58
+
59
+ useClickOutside(dropdownRef, () => setIsOpen(false));
60
+
61
+ return (
62
+ <div ref={dropdownRef}>
63
+ <button onClick={() => setIsOpen(true)}>Open</button>
64
+ {isOpen && <ul><li>Option 1</li><li>Option 2</li></ul>}
65
+ </div>
66
+ );
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ### useDebounce
73
+
74
+ Hook that debounces a value by a given delay. The debounced value will only update after the specified delay has passed since the last change. Useful for search inputs, API calls, and any rapid-fire updates.
75
+
76
+ **Type Parameters:**
77
+ - `T` - The type of the value to debounce
78
+
79
+ **Parameters:**
80
+ - `value: T` - The value to debounce
81
+ - `delay: number` - The debounce delay in milliseconds
82
+
83
+ **Returns:**
84
+ - `T` - The debounced value
85
+
86
+ **Example:**
87
+ ```tsx
88
+ import { useDebounce } from 'react-essentials-functions';
89
+ import { useState, useEffect } from 'react';
90
+
91
+ function SearchComponent() {
92
+ const [searchTerm, setSearchTerm] = useState('');
93
+ const debouncedSearch = useDebounce(searchTerm, 300);
94
+
95
+ useEffect(() => {
96
+ if (debouncedSearch) {
97
+ // This only fires 300ms after the user stops typing
98
+ fetchResults(debouncedSearch);
99
+ }
100
+ }, [debouncedSearch]);
101
+
102
+ return (
103
+ <input
104
+ value={searchTerm}
105
+ onChange={(e) => setSearchTerm(e.target.value)}
106
+ placeholder="Search..."
107
+ />
108
+ );
109
+ }
110
+ ```
111
+
112
+ ---
113
+
114
+ ### useDimensions
115
+
116
+ Hook to get the dimensions of a DOM element. Uses `ResizeObserver` for optimal performance with a fallback to window events for older browsers.
117
+
118
+ **Parameters:**
119
+ - `targetRef: RefObject<HTMLElement>` - React ref to the element to measure
120
+
121
+ **Returns:**
122
+ - `Dimensions` - Object containing `width` and `height` of the element
123
+
124
+ **Side effects:**
125
+ - Creates a `ResizeObserver` on the target element (or falls back to `resize`/`scroll` window listeners)
126
+ - Cleans up on unmount
127
+
128
+ **Example:**
129
+ ```tsx
130
+ import { useDimensions } from 'react-essentials-functions';
131
+ import { useRef } from 'react';
132
+
133
+ function MyComponent() {
134
+ const targetRef = useRef<HTMLDivElement>(null);
135
+ const { width, height } = useDimensions(targetRef);
136
+
137
+ return (
138
+ <div ref={targetRef}>
139
+ Size: {width}px x {height}px
140
+ </div>
141
+ );
142
+ }
143
+ ```
144
+
145
+ ---
146
+
147
+ ### useLocalStorage
148
+
149
+ Hook that syncs state with `localStorage`. Handles JSON serialization/deserialization automatically. Falls back gracefully when `localStorage` is unavailable (SSR, private browsing).
150
+
151
+ **Type Parameters:**
152
+ - `T` - The type of the stored value
153
+
154
+ **Parameters:**
155
+ - `key: string` - The localStorage key
156
+ - `initialValue: T` - The initial value if nothing is stored
157
+
158
+ **Returns:**
159
+ - `[T, (value: T | ((prev: T) => T)) => void, () => void]` - A tuple containing:
160
+ - The current stored value
161
+ - A setter function (accepts value or updater function)
162
+ - A remove function to clear the key from localStorage
163
+
164
+ **Side effects:**
165
+ - Reads from `localStorage` on initialization
166
+ - Writes to `localStorage` on every value change
167
+ - `removeValue` deletes the key from `localStorage`
168
+
169
+ **Example:**
170
+ ```tsx
171
+ import { useLocalStorage } from 'react-essentials-functions';
172
+
173
+ function Settings() {
174
+ const [name, setName, removeName] = useLocalStorage('user-name', '');
175
+ const [preferences, setPreferences] = useLocalStorage('prefs', {
176
+ notifications: true,
177
+ language: 'en',
178
+ });
179
+
180
+ return (
181
+ <div>
182
+ <input value={name} onChange={(e) => setName(e.target.value)} />
183
+ <button onClick={removeName}>Clear name</button>
184
+ <button onClick={() => setPreferences(prev => ({ ...prev, language: 'fr' }))}>
185
+ Switch to French
186
+ </button>
187
+ </div>
188
+ );
189
+ }
190
+ ```
191
+
192
+ ---
193
+
194
+ ### useMediaQuery
195
+
196
+ Hook that tracks whether a CSS media query matches. Listens for changes and updates automatically. Useful for responsive design, detecting dark mode preference, reduced motion, etc.
197
+
198
+ **Parameters:**
199
+ - `query: string` - The CSS media query string (e.g. `'(min-width: 768px)'`)
200
+
201
+ **Returns:**
202
+ - `boolean` - Whether the media query currently matches
203
+
204
+ **Side effects:**
205
+ - Adds a `change` listener on the `MediaQueryList` object
206
+ - Removes listener on unmount or query change
207
+
208
+ **Example:**
209
+ ```tsx
210
+ import { useMediaQuery } from 'react-essentials-functions';
211
+
212
+ function ResponsiveComponent() {
213
+ const isMobile = useMediaQuery('(max-width: 767px)');
214
+ const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
215
+ const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
216
+
217
+ return (
218
+ <div>
219
+ {isMobile ? <MobileLayout /> : <DesktopLayout />}
220
+ {prefersDark && <span>Dark mode detected</span>}
221
+ </div>
222
+ );
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ### usePrevious
229
+
230
+ Hook that returns the previous value of a variable. Useful for comparing current and previous props or state values.
231
+
232
+ **Type Parameters:**
233
+ - `T` - The type of the tracked value
234
+
235
+ **Parameters:**
236
+ - `value: T` - The value to track
237
+
238
+ **Returns:**
239
+ - `T | undefined` - The value from the previous render, or `undefined` on first render
240
+
241
+ **Example:**
242
+ ```tsx
243
+ import { usePrevious } from 'react-essentials-functions';
244
+ import { useState } from 'react';
245
+
246
+ function Counter() {
247
+ const [count, setCount] = useState(0);
248
+ const previousCount = usePrevious(count);
249
+
250
+ return (
251
+ <div>
252
+ <p>Current: {count}, Previous: {previousCount ?? 'N/A'}</p>
253
+ <button onClick={() => setCount(count + 1)}>Increment</button>
254
+ </div>
255
+ );
256
+ }
257
+ ```
258
+
259
+ ---
260
+
261
+ ### useSafeFetch
262
+
263
+ Hook that provides a fetch function which automatically aborts previous requests and cleans up on unmount using `AbortController`. Prevents race conditions when multiple requests are made in sequence.
264
+
265
+ **Parameters:**
266
+ - None
267
+
268
+ **Returns:**
269
+ - `(url: string, options?: RequestInit) => Promise<Response>` - Fetch function with automatic abort handling
270
+
271
+ **Side effects:**
272
+ - Aborts the previous in-flight request when a new one is made
273
+ - Aborts any pending request on component unmount
274
+ - User-provided `signal` in options is ignored (the hook manages its own)
275
+
276
+ **Example:**
277
+ ```tsx
278
+ import { useSafeFetch } from 'react-essentials-functions';
279
+ import { useEffect } from 'react';
280
+
281
+ function DataComponent() {
282
+ const safeFetch = useSafeFetch();
283
+
284
+ useEffect(() => {
285
+ const fetchData = async () => {
286
+ try {
287
+ const response = await safeFetch('https://api.example.com/data');
288
+ const data = await response.json();
289
+ } catch (error) {
290
+ if (error.name !== 'AbortError') {
291
+ console.error('Fetch error:', error);
292
+ }
293
+ }
294
+ };
295
+
296
+ fetchData();
297
+ }, [safeFetch]);
298
+
299
+ return <div>Loading data...</div>;
300
+ }
301
+ ```
302
+
303
+ ---
304
+
305
+ ### useSafeState
306
+
307
+ A version of `useState` that prevents state updates after the component unmounts, preventing memory leaks and "Can't perform a React state update on an unmounted component" warnings.
308
+
309
+ **Type Parameters:**
310
+ - `T` - The type of the state value
311
+
312
+ **Parameters:**
313
+ - `initialValue: T | (() => T)` - The initial state value
314
+
315
+ **Returns:**
316
+ - `[T, (value: T | ((prevState: T) => T)) => void]` - A tuple containing the current state and a safe setState function
317
+
318
+ **Example:**
319
+ ```tsx
320
+ import { useSafeState } from 'react-essentials-functions';
321
+ import { useEffect } from 'react';
322
+
323
+ function UserProfile({ userId }) {
324
+ const [user, setUser] = useSafeState<User | null>(null);
325
+
326
+ useEffect(() => {
327
+ fetchUser(userId).then((data) => {
328
+ // Safe even if component unmounted during fetch
329
+ setUser(data);
330
+ });
331
+ }, [userId]);
332
+
333
+ return user ? <div>{user.name}</div> : <div>Loading...</div>;
334
+ }
335
+ ```
336
+
337
+ ---
338
+
339
+ ### useScript
340
+
341
+ Hook to dynamically load external scripts with status tracking and callback support.
342
+
343
+ **Parameters:**
344
+ - `url: string` - The URL of the script to load
345
+ - `options?: UseScriptOptions` - Optional configuration:
346
+ - `onLoad?: () => void` - Callback when script loads successfully
347
+ - `onError?: () => void` - Callback when script fails to load
348
+ - `removeOnUnmount?: boolean` - Whether to remove script on unmount (default: `true`)
349
+
350
+ **Returns:**
351
+ - `UseScriptStatus` - The current status: `'idle' | 'loading' | 'ready' | 'error'`
352
+
353
+ **Side effects:**
354
+ - Appends a `<script>` tag to `document.body`
355
+ - Removes the script tag on unmount (unless `removeOnUnmount: false`)
356
+ - Detects and reuses already-loaded scripts
357
+
358
+ **Example:**
359
+ ```tsx
360
+ import { useScript } from 'react-essentials-functions';
361
+
362
+ function GoogleMapsComponent() {
363
+ const status = useScript('https://maps.googleapis.com/maps/api/js', {
364
+ onLoad: () => console.log('Google Maps loaded'),
365
+ onError: () => console.error('Failed to load Google Maps'),
366
+ });
367
+
368
+ if (status === 'loading') return <div>Loading map...</div>;
369
+ if (status === 'error') return <div>Error loading map</div>;
370
+ if (status === 'idle') return <div>Initializing...</div>;
371
+
372
+ return <div>Map is ready!</div>;
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ### useTheme
379
+
380
+ Hook to manage theme (light/dark) with `localStorage` persistence and SSR support. Automatically detects system color scheme preference via `prefers-color-scheme` when no theme has been previously stored.
381
+
382
+ **Returns:**
383
+ - `[ThemeMode, () => void, boolean]` - A tuple containing:
384
+ - Current theme mode (`'light' | 'dark'`)
385
+ - Function to toggle between themes
386
+ - Boolean indicating if component is mounted (useful for SSR)
387
+
388
+ **Side effects:**
389
+ - Reads/writes to `localStorage` with key `'theme'`
390
+ - Detects system `prefers-color-scheme` preference on first load
391
+
392
+ **Example:**
393
+ ```tsx
394
+ import { useTheme } from 'react-essentials-functions';
395
+
396
+ function ThemeToggle() {
397
+ const [theme, toggleTheme, mounted] = useTheme();
398
+
399
+ // Avoid hydration mismatch
400
+ if (!mounted) return null;
401
+
402
+ return (
403
+ <button onClick={toggleTheme}>
404
+ Switch to {theme === 'light' ? 'dark' : 'light'} mode
405
+ </button>
406
+ );
407
+ }
408
+ ```
409
+
410
+ ---
411
+
412
+ ### useToggle
413
+
414
+ Hook for managing a boolean toggle state. Provides a simple API for toggling, setting true, or setting false. Useful for modals, dropdowns, accordions, etc.
415
+
416
+ **Parameters:**
417
+ - `initialValue?: boolean` - The initial boolean value (default: `false`)
418
+
419
+ **Returns:**
420
+ - `[boolean, () => void, () => void, () => void]` - A tuple containing:
421
+ - The current boolean value
422
+ - `toggle` - Flips the value
423
+ - `setTrue` - Sets to `true`
424
+ - `setFalse` - Sets to `false`
425
+
426
+ **Example:**
427
+ ```tsx
428
+ import { useToggle } from 'react-essentials-functions';
429
+
430
+ function Modal() {
431
+ const [isOpen, toggleOpen, open, close] = useToggle(false);
432
+
433
+ return (
434
+ <div>
435
+ <button onClick={open}>Open Modal</button>
436
+ {isOpen && (
437
+ <div className="modal">
438
+ <p>Modal content</p>
439
+ <button onClick={close}>Close</button>
440
+ </div>
441
+ )}
442
+ </div>
443
+ );
444
+ }
445
+ ```
446
+
447
+ ---
448
+
449
+ ### useWindowDimensions
450
+
451
+ Hook to get the current window dimensions with automatic updates on resize. SSR-safe (returns `0` for both dimensions when `window` is unavailable).
452
+
453
+ **Returns:**
454
+ - `WindowDimensions` - Object containing `width` and `height` of the window
455
+
456
+ **Side effects:**
457
+ - Adds a `resize` event listener on `window`
458
+ - Removes listener on unmount
459
+
460
+ **Example:**
461
+ ```tsx
462
+ import { useWindowDimensions } from 'react-essentials-functions';
463
+
464
+ function ResponsiveComponent() {
465
+ const { width, height } = useWindowDimensions();
466
+
467
+ return (
468
+ <div>
469
+ Window size: {width}px x {height}px
470
+ {width < 768 ? <MobileLayout /> : <DesktopLayout />}
471
+ </div>
472
+ );
473
+ }
474
+ ```
475
+
476
+ ---
477
+
478
+ ## Components
479
+
480
+ ### ConditionalWrapper
481
+
482
+ Component that conditionally wraps its children with a wrapper component based on a condition. When the condition is `false`, children are rendered unwrapped inside a fragment.
483
+
484
+ **Props:**
485
+ - `condition: boolean` - Whether to wrap the children
486
+ - `wrapper: (children: React.ReactNode) => JSX.Element` - Function that returns the wrapper element
487
+ - `children: React.ReactNode` - Children to wrap
488
+
489
+ **Example:**
490
+ ```tsx
491
+ import { ConditionalWrapper } from 'react-essentials-functions';
492
+
493
+ function LinkWrapper({ link, children }) {
494
+ return (
495
+ <ConditionalWrapper
496
+ condition={!!link}
497
+ wrapper={(c) => <a href={link}>{c}</a>}
498
+ >
499
+ <button>{children}</button>
500
+ </ConditionalWrapper>
501
+ );
502
+ }
503
+ ```
504
+
505
+ ---
506
+
507
+ ## TypeScript Support
508
+
509
+ This library is written in TypeScript and includes full type definitions. All types are exported for your convenience:
510
+
511
+ ```ts
512
+ import type {
513
+ Dimensions,
514
+ WindowDimensions,
515
+ ThemeMode,
516
+ UseScriptStatus,
517
+ UseScriptOptions,
518
+ ConditionalWrapperProps,
519
+ } from 'react-essentials-functions';
520
+ ```
521
+
522
+ ## License
523
+
524
+ MIT
@@ -1,7 +1,24 @@
1
1
  import * as React from 'react';
2
- export declare type ConditionnalWrapperProps = {
2
+ export type ConditionalWrapperProps = {
3
3
  condition: boolean;
4
- wrapper: (children: React.ReactNode) => React.ReactNode;
4
+ wrapper: (children: React.ReactNode) => JSX.Element;
5
5
  children: React.ReactNode;
6
6
  };
7
- export declare const ConditionalWrapper: ({ condition, wrapper, children, }: ConditionnalWrapperProps) => React.ReactNode;
7
+ /**
8
+ * Component that conditionally wraps its children with a wrapper component.
9
+ *
10
+ * @param condition - Whether to wrap the children
11
+ * @param wrapper - Function that returns the wrapper element
12
+ * @param children - Children to wrap
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <ConditionalWrapper
17
+ * condition={!!link}
18
+ * wrapper={(children) => <a href={link}>{children}</a>}
19
+ * >
20
+ * <button>Click me</button>
21
+ * </ConditionalWrapper>
22
+ * ```
23
+ */
24
+ export declare const ConditionalWrapper: ({ condition, wrapper, children, }: ConditionalWrapperProps) => JSX.Element;
@@ -1,11 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConditionalWrapper = void 0;
4
- const ConditionalWrapper = ({ condition, wrapper, children, }) => (condition ? wrapper(children) : children);
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /**
6
+ * Component that conditionally wraps its children with a wrapper component.
7
+ *
8
+ * @param condition - Whether to wrap the children
9
+ * @param wrapper - Function that returns the wrapper element
10
+ * @param children - Children to wrap
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <ConditionalWrapper
15
+ * condition={!!link}
16
+ * wrapper={(children) => <a href={link}>{children}</a>}
17
+ * >
18
+ * <button>Click me</button>
19
+ * </ConditionalWrapper>
20
+ * ```
21
+ */
22
+ const ConditionalWrapper = ({ condition, wrapper, children, }) => condition ? wrapper(children) : (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
5
23
  exports.ConditionalWrapper = ConditionalWrapper;
6
- // <ConditionalWrapper
7
- // condition={link}
8
- // wrapper={children => <a href={link}>{children}</a>}
9
- // >
10
- // .....
11
- // </ConditionalWrapper>
@@ -1,6 +1,12 @@
1
+ export * from './useClickOutside';
2
+ export * from './useDebounce';
1
3
  export * from './useDimensions';
4
+ export * from './useLocalStorage';
5
+ export * from './useMediaQuery';
6
+ export * from './usePrevious';
2
7
  export * from './useSafeFetch';
3
8
  export * from './useSafeState';
4
9
  export * from './useScript';
5
10
  export * from './useTheme';
11
+ export * from './useToggle';
6
12
  export * from './useWindowDimensions';
@@ -14,9 +14,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./useClickOutside"), exports);
18
+ __exportStar(require("./useDebounce"), exports);
17
19
  __exportStar(require("./useDimensions"), exports);
20
+ __exportStar(require("./useLocalStorage"), exports);
21
+ __exportStar(require("./useMediaQuery"), exports);
22
+ __exportStar(require("./usePrevious"), exports);
18
23
  __exportStar(require("./useSafeFetch"), exports);
19
24
  __exportStar(require("./useSafeState"), exports);
20
25
  __exportStar(require("./useScript"), exports);
21
26
  __exportStar(require("./useTheme"), exports);
27
+ __exportStar(require("./useToggle"), exports);
22
28
  __exportStar(require("./useWindowDimensions"), exports);
@@ -0,0 +1,23 @@
1
+ import { RefObject } from 'react';
2
+ /**
3
+ * Hook that detects clicks outside of a referenced element.
4
+ * Useful for closing dropdowns, modals, and popovers.
5
+ *
6
+ * @param ref - React ref to the element to monitor
7
+ * @param handler - Callback fired when a click outside is detected
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * const dropdownRef = useRef<HTMLDivElement>(null);
12
+ * const [isOpen, setIsOpen] = useState(false);
13
+ *
14
+ * useClickOutside(dropdownRef, () => setIsOpen(false));
15
+ *
16
+ * return (
17
+ * <div ref={dropdownRef}>
18
+ * {isOpen && <DropdownMenu />}
19
+ * </div>
20
+ * );
21
+ * ```
22
+ */
23
+ export declare function useClickOutside(ref: RefObject<HTMLElement>, handler: (event: MouseEvent | TouchEvent) => void): void;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useClickOutside = useClickOutside;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Hook that detects clicks outside of a referenced element.
7
+ * Useful for closing dropdowns, modals, and popovers.
8
+ *
9
+ * @param ref - React ref to the element to monitor
10
+ * @param handler - Callback fired when a click outside is detected
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const dropdownRef = useRef<HTMLDivElement>(null);
15
+ * const [isOpen, setIsOpen] = useState(false);
16
+ *
17
+ * useClickOutside(dropdownRef, () => setIsOpen(false));
18
+ *
19
+ * return (
20
+ * <div ref={dropdownRef}>
21
+ * {isOpen && <DropdownMenu />}
22
+ * </div>
23
+ * );
24
+ * ```
25
+ */
26
+ function useClickOutside(ref, handler) {
27
+ (0, react_1.useEffect)(() => {
28
+ const listener = (event) => {
29
+ const element = ref.current;
30
+ if (!element || element.contains(event.target)) {
31
+ return;
32
+ }
33
+ handler(event);
34
+ };
35
+ document.addEventListener('mousedown', listener);
36
+ document.addEventListener('touchstart', listener);
37
+ return () => {
38
+ document.removeEventListener('mousedown', listener);
39
+ document.removeEventListener('touchstart', listener);
40
+ };
41
+ }, [ref, handler]);
42
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Hook that debounces a value by a given delay.
3
+ * The debounced value will only update after the specified delay has passed
4
+ * since the last change.
5
+ *
6
+ * @param value - The value to debounce
7
+ * @param delay - The debounce delay in milliseconds
8
+ * @returns The debounced value
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const [searchTerm, setSearchTerm] = useState('');
13
+ * const debouncedSearch = useDebounce(searchTerm, 300);
14
+ *
15
+ * useEffect(() => {
16
+ * // This will only fire 300ms after the user stops typing
17
+ * fetchResults(debouncedSearch);
18
+ * }, [debouncedSearch]);
19
+ * ```
20
+ */
21
+ export declare function useDebounce<T>(value: T, delay: number): T;