@alessiofrittoli/react-hooks 1.2.0 → 2.0.1

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.
Files changed (96) hide show
  1. package/README.md +387 -73
  2. package/dist/eslint.js +1 -1
  3. package/dist/eslint.mjs +1 -1
  4. package/dist/index.d.mts +940 -11
  5. package/dist/index.d.ts +940 -11
  6. package/dist/index.js +1 -1
  7. package/dist/index.mjs +1 -1
  8. package/package.json +26 -34
  9. package/dist/browser-api/index.d.mts +0 -6
  10. package/dist/browser-api/index.d.ts +0 -6
  11. package/dist/browser-api/index.js +0 -1
  12. package/dist/browser-api/index.mjs +0 -1
  13. package/dist/browser-api/storage/index.d.mts +0 -4
  14. package/dist/browser-api/storage/index.d.ts +0 -4
  15. package/dist/browser-api/storage/index.js +0 -1
  16. package/dist/browser-api/storage/index.mjs +0 -1
  17. package/dist/browser-api/storage/useLocalStorage.d.mts +0 -11
  18. package/dist/browser-api/storage/useLocalStorage.d.ts +0 -11
  19. package/dist/browser-api/storage/useLocalStorage.js +0 -1
  20. package/dist/browser-api/storage/useLocalStorage.mjs +0 -1
  21. package/dist/browser-api/storage/useSessionStorage.d.mts +0 -11
  22. package/dist/browser-api/storage/useSessionStorage.d.ts +0 -11
  23. package/dist/browser-api/storage/useSessionStorage.js +0 -1
  24. package/dist/browser-api/storage/useSessionStorage.mjs +0 -1
  25. package/dist/browser-api/storage/useStorage.d.mts +0 -12
  26. package/dist/browser-api/storage/useStorage.d.ts +0 -12
  27. package/dist/browser-api/storage/useStorage.js +0 -1
  28. package/dist/browser-api/storage/useStorage.mjs +0 -1
  29. package/dist/browser-api/useIsPortrait.d.mts +0 -10
  30. package/dist/browser-api/useIsPortrait.d.ts +0 -10
  31. package/dist/browser-api/useIsPortrait.js +0 -1
  32. package/dist/browser-api/useIsPortrait.mjs +0 -1
  33. package/dist/browser-api/useMediaQuery.d.mts +0 -11
  34. package/dist/browser-api/useMediaQuery.d.ts +0 -11
  35. package/dist/browser-api/useMediaQuery.js +0 -1
  36. package/dist/browser-api/useMediaQuery.mjs +0 -1
  37. package/dist/chunk-26XSIIUK.mjs +0 -1
  38. package/dist/chunk-2CZNCZFR.mjs +0 -0
  39. package/dist/chunk-43RVMR6S.mjs +0 -1
  40. package/dist/chunk-4BGMCO3F.js +0 -1
  41. package/dist/chunk-65SF33P4.mjs +0 -1
  42. package/dist/chunk-6BSD56XC.js +0 -1
  43. package/dist/chunk-6IRIG5HU.mjs +0 -1
  44. package/dist/chunk-7ILYMOVB.js +0 -1
  45. package/dist/chunk-AJKPRTOJ.mjs +0 -1
  46. package/dist/chunk-EFFJB2GR.js +0 -1
  47. package/dist/chunk-FUCUVFLW.js +0 -1
  48. package/dist/chunk-G55NP3QG.js +0 -1
  49. package/dist/chunk-HO57SPST.js +0 -1
  50. package/dist/chunk-HZLWSH3W.mjs +0 -1
  51. package/dist/chunk-IDNLFWFO.js +0 -1
  52. package/dist/chunk-RENUP2VU.mjs +0 -1
  53. package/dist/chunk-SNMJ53QB.js +0 -1
  54. package/dist/chunk-SW7BCMZF.js +0 -1
  55. package/dist/chunk-TA6PCHH4.js +0 -1
  56. package/dist/chunk-UAJXVBNR.mjs +0 -1
  57. package/dist/chunk-UXNKH2MC.mjs +0 -1
  58. package/dist/chunk-VLSP3MAN.js +0 -1
  59. package/dist/chunk-WLIFJNZA.mjs +0 -0
  60. package/dist/chunk-WOVEE75M.js +0 -1
  61. package/dist/chunk-YAZXPL6I.mjs +0 -1
  62. package/dist/chunk-YLUTFORB.js +0 -1
  63. package/dist/chunk-YQO25XKN.mjs +0 -0
  64. package/dist/chunk-ZNAB4TYW.mjs +0 -0
  65. package/dist/dom-api/index.d.mts +0 -2
  66. package/dist/dom-api/index.d.ts +0 -2
  67. package/dist/dom-api/index.js +0 -1
  68. package/dist/dom-api/index.mjs +0 -1
  69. package/dist/dom-api/useFocusTrap.d.mts +0 -15
  70. package/dist/dom-api/useFocusTrap.d.ts +0 -15
  71. package/dist/dom-api/useFocusTrap.js +0 -1
  72. package/dist/dom-api/useFocusTrap.mjs +0 -1
  73. package/dist/dom-api/useScrollBlock.d.mts +0 -8
  74. package/dist/dom-api/useScrollBlock.d.ts +0 -8
  75. package/dist/dom-api/useScrollBlock.js +0 -1
  76. package/dist/dom-api/useScrollBlock.mjs +0 -1
  77. package/dist/misc/index.d.mts +0 -3
  78. package/dist/misc/index.d.ts +0 -3
  79. package/dist/misc/index.js +0 -1
  80. package/dist/misc/index.mjs +0 -1
  81. package/dist/misc/useIsClient.d.mts +0 -8
  82. package/dist/misc/useIsClient.d.ts +0 -8
  83. package/dist/misc/useIsClient.js +0 -1
  84. package/dist/misc/useIsClient.mjs +0 -1
  85. package/dist/misc/useIsFirstRender.d.mts +0 -9
  86. package/dist/misc/useIsFirstRender.d.ts +0 -9
  87. package/dist/misc/useIsFirstRender.js +0 -1
  88. package/dist/misc/useIsFirstRender.mjs +0 -1
  89. package/dist/misc/usePagination.d.mts +0 -12
  90. package/dist/misc/usePagination.d.ts +0 -12
  91. package/dist/misc/usePagination.js +0 -1
  92. package/dist/misc/usePagination.mjs +0 -1
  93. package/dist/misc/useUpdateEffect.d.mts +0 -9
  94. package/dist/misc/useUpdateEffect.d.ts +0 -9
  95. package/dist/misc/useUpdateEffect.js +0 -1
  96. package/dist/misc/useUpdateEffect.mjs +0 -1
package/README.md CHANGED
@@ -25,8 +25,21 @@
25
25
  - [ESLint Configuration](#eslint-configuration)
26
26
  - [API Reference](#api-reference)
27
27
  - [Browser API](#browser-api)
28
+ - [`useStorage`](#usestorage)
29
+ - [`useLocalStorage`](#uselocalstorage)
30
+ - [`useSessionStorage`](#usesessionstorage)
31
+ - [`useMediaQuery`](#usemediaquery)
32
+ - [`useDarkMode`](#usedarkmode)
33
+ - [`useIsPortrait`](#useisportrait)
28
34
  - [DOM API](#dom-api)
35
+ - [`useScrollBlock`](#usescrollblock)
36
+ - [`useFocusTrap`](#usefocustrap)
37
+ - [`useInView`](#useinview)
29
38
  - [Miscellaneous](#miscellaneous)
39
+ - [`useIsClient`](#useisclient)
40
+ - [`useIsFirstRender`](#useisfirstrender)
41
+ - [`useUpdateEffect`](#useupdateeffect)
42
+ - [`usePagination`](#usepagination)
30
43
  - [Development](#development)
31
44
  - [Install depenendencies](#install-depenendencies)
32
45
  - [Build the source code](#build-the-source-code)
@@ -105,9 +118,9 @@ Easly handle Local or Session Storage State.
105
118
 
106
119
  | Parameter | Type | Default | Description |
107
120
  |-----------|------|---------|-------------|
108
- | `key` | `string` | - | The storage item key. |
109
- | `initialValue` | `T` | - | The storage item initial value. |
110
- | `type` | `local\|session` | local | (Optional) The storage API to use. |
121
+ | `key` | `string` | - | The storage item key. |
122
+ | `initial` | `T` | - | The storage item initial value. |
123
+ | `type` | `local\|session` | local | (Optional) The storage API to use. |
111
124
 
112
125
  </details>
113
126
 
@@ -135,14 +148,6 @@ A tuple with the stored item value or initial value and the setter function.
135
148
  import {
136
149
  useStorage, useLocalStorage, useSessionStorage
137
150
  } from '@alessiofrittoli/react-hooks'
138
- // or
139
- import {
140
- useStorage, useLocalStorage, useSessionStorage
141
- } from '@alessiofrittoli/react-hooks/browser-api'
142
- // or
143
- import {
144
- useStorage, useLocalStorage, useSessionStorage
145
- } from '@alessiofrittoli/react-hooks/browser-api/storage'
146
151
  ```
147
152
 
148
153
  ---
@@ -152,7 +157,7 @@ import {
152
157
  ```tsx
153
158
  'use client'
154
159
 
155
- import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
160
+ import { useStorage } from '@alessiofrittoli/react-hooks'
156
161
 
157
162
  type Locale = 'it' | 'en'
158
163
 
@@ -178,7 +183,7 @@ export const SomeComponent: React.FC = () => {
178
183
  'use client'
179
184
 
180
185
  import { useCallback } from 'react'
181
- import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
186
+ import { useStorage } from '@alessiofrittoli/react-hooks'
182
187
 
183
188
  type Locale = 'it' | 'en'
184
189
 
@@ -208,7 +213,7 @@ export const LanguageSwitcher: React.FC = () => {
208
213
  'use client'
209
214
 
210
215
  import { useCallback } from 'react'
211
- import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
216
+ import { useStorage } from '@alessiofrittoli/react-hooks'
212
217
 
213
218
  type Locale = 'it' | 'en'
214
219
 
@@ -287,20 +292,165 @@ Type: `boolean`
287
292
 
288
293
  <summary style="cursor:pointer">Usage</summary>
289
294
 
290
- ###### Importing the hook
295
+ ###### Check if user device prefers dark color scheme
291
296
 
292
297
  ```tsx
293
298
  import { useMediaQuery } from '@alessiofrittoli/react-hooks'
294
- // or
295
- import { useMediaQuery } from '@alessiofrittoli/react-hooks/browser-api'
299
+
300
+ const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
296
301
  ```
297
302
 
303
+ </details>
304
+
298
305
  ---
299
306
 
300
- ###### Check if user device prefers dark color scheme
307
+ ##### `useDarkMode`
308
+
309
+ Easily manage dark mode with full respect for user device preferences.
310
+
311
+ This hook is user-oriented and built to honor system-level color scheme settings:
312
+
313
+ - If the device prefers a dark color scheme, dark mode is automatically enabled on first load.
314
+ - If the user enables/disables dark mode via a web widget, the preference is stored in `localStorage` under the key `dark-mode`.
315
+ - If the device color scheme preference changes (e.g. via OS settings), that change takes precedence and is stored for future visits.
316
+
317
+ <details>
318
+
319
+ <summary style="cursor:pointer">Parameters</summary>
320
+
321
+ | Parameter | Type | Description |
322
+ |-----------|------|-------------|
323
+ | `options` | `UseDarkModeOptions` | (Optional) Configuration object for the hook. |
324
+ | `options.initial` | `boolean` | (Optional) The fallback value to use if no preference is saved in `localStorage`. Defaults to `true` if the device prefers dark mode. |
325
+ | `options.docClassNames` | `[dark: string, light: string]` | (Optional) Array of class names to toggle on the `<html>` element, e.g. `['dark', 'light']`. |
326
+
327
+ </details>
328
+
329
+ ---
330
+
331
+ <details>
332
+
333
+ <summary style="cursor:pointer">Returns</summary>
334
+
335
+ Type: `UseDarkModeOutput`
336
+
337
+ An object containing utilities for managing dark mode:
338
+
339
+ - `isDarkMode`: `boolean` — Whether dark mode is currently enabled.
340
+ - `isDarkOS`: `boolean` — Whether the user's system prefers dark mode.
341
+ - `toggleDarkMode`: `() => void` — Toggles dark mode and saves the preference.
342
+ - `enableDarkMode`: `() => void` — Enables dark mode and saves the preference.
343
+ - `disableDarkMode`: `() => void` — Disables dark mode and saves the preference.
344
+
345
+ </details>
346
+
347
+ ---
348
+
349
+ <details>
350
+
351
+ <summary style="cursor:pointer">Usage</summary>
352
+
353
+ ###### Basic usage
301
354
 
302
355
  ```tsx
303
- const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
356
+ 'use client'
357
+
358
+ import { useDarkMode } from '@alessiofrittoli/react-hooks'
359
+
360
+ export const Component: React.FC = () => {
361
+ const { isDarkMode } = useDarkMode()
362
+
363
+ return (
364
+ <div>{ isDarkMode ? 'Dark mode enabled' : 'Dark mode disabled' }</div>
365
+ )
366
+ }
367
+ ```
368
+
369
+ ---
370
+
371
+ ###### Update Document class names for CSS styling
372
+
373
+ ```tsx
374
+ // Component.tsx
375
+ 'use client'
376
+
377
+ import { useDarkMode } from '@alessiofrittoli/react-hooks'
378
+
379
+ export const Component: React.FC = () => {
380
+ const { isDarkMode } = useDarkMode( {
381
+ docClassNames: [ 'dark', 'light' ],
382
+ } )
383
+
384
+ return (
385
+ <div>{ isDarkMode ? 'Dark mode enabled' : 'Dark mode disabled' }</div>
386
+ )
387
+ }
388
+ ```
389
+
390
+ ```css
391
+ /* style.css */
392
+ .light {
393
+ color-scheme: light;
394
+ }
395
+
396
+ .dark {
397
+ color-scheme: dark;
398
+ }
399
+
400
+ .light body
401
+ {
402
+ color : black;
403
+ background: white;
404
+ }
405
+
406
+ .dark body
407
+ {
408
+ color : white;
409
+ background: black;
410
+ }
411
+ ```
412
+
413
+ ---
414
+
415
+ ###### Custom theme switcher
416
+
417
+ ```tsx
418
+ 'use client'
419
+
420
+ import { useDarkMode } from '@alessiofrittoli/react-hooks'
421
+
422
+ export const ThemeSwitcher: React.FC = () => {
423
+ const { isDarkMode, toggleDarkMode } = useDarkMode()
424
+
425
+ return (
426
+ <button onClick={ toggleDarkMode }>
427
+ { isDarkMode ? '🌙' : '☀️' }
428
+ </button>
429
+ )
430
+ }
431
+ ```
432
+
433
+ ---
434
+
435
+ ###### Sync Document theme-color for consistent browser styling
436
+
437
+ Browsers automatically apply colorization using:
438
+
439
+ ```html
440
+ <meta name='theme-color' media='(prefers-color-scheme: dark)' />
441
+ ```
442
+
443
+ This works based on the OS preference — *not your site theme*. That can cause mismatches if, for example, the system is in dark mode but the user disabled dark mode via a web toggle.
444
+
445
+ To ensure consistency, `useDarkMode` updates these meta tags dynamically based on the actual mode.
446
+
447
+ Just make sure to define both `light` and `dark` theme-color tags in your document:
448
+
449
+ ```html
450
+ <head>
451
+ <meta name='theme-color' media='(prefers-color-scheme: light)' content='lime'>
452
+ <meta name='theme-color' media='(prefers-color-scheme: dark)' content='aqua'>
453
+ </head>
304
454
  ```
305
455
 
306
456
  </details>
@@ -330,19 +480,11 @@ Type: `boolean`
330
480
 
331
481
  <summary style="cursor:pointer">Usage</summary>
332
482
 
333
- ###### Importing the hook
483
+ ###### Check if user device is in landscape
334
484
 
335
485
  ```tsx
336
486
  import { useIsPortrait } from '@alessiofrittoli/react-hooks'
337
- // or
338
- import { useIsPortrait } from '@alessiofrittoli/react-hooks/browser-api'
339
- ```
340
487
 
341
- ---
342
-
343
- ###### Check if user device is in landscape
344
-
345
- ```tsx
346
488
  const isLandscape = ! useIsPortrait()
347
489
  ```
348
490
 
@@ -384,19 +526,11 @@ A tuple with block and restore scroll callbacks.
384
526
 
385
527
  <summary style="cursor:pointer">Usage</summary>
386
528
 
387
- ###### Importing the hook
529
+ ###### Block Document Overflow
388
530
 
389
531
  ```tsx
390
532
  import { useScrollBlock } from '@alessiofrittoli/react-hooks'
391
- // or
392
- import { useScrollBlock } from '@alessiofrittoli/react-hooks/dom-api'
393
- ```
394
533
 
395
- ---
396
-
397
- ###### Block Document Overflow
398
-
399
- ```tsx
400
534
  const [ blockScroll, restoreScroll ] = useScrollBlock()
401
535
 
402
536
  const openPopUpHandler = useCallback( () => {
@@ -476,19 +610,11 @@ A tuple containing:
476
610
 
477
611
  <summary style="cursor:pointer">Usage</summary>
478
612
 
479
- ###### Importing the hook
613
+ ###### Defining the target on hook initialization
480
614
 
481
615
  ```tsx
482
616
  import { useFocusTrap } from '@alessiofrittoli/react-hooks'
483
- // or
484
- import { useFocusTrap } from '@alessiofrittoli/react-hooks/dom-api'
485
- ```
486
617
 
487
- ---
488
-
489
- ###### Defining the target on hook initialization
490
-
491
- ```tsx
492
618
  const modalRef = useRef<HTMLDivElement>( null )
493
619
  const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap( modalRef )
494
620
 
@@ -510,6 +636,8 @@ const modalCloseHandler = useCallback( () => {
510
636
  ###### Defining the target ondemand
511
637
 
512
638
  ```tsx
639
+ import { useFocusTrap } from '@alessiofrittoli/react-hooks'
640
+
513
641
  const modalRef = useRef<HTMLDivElement>( null )
514
642
  const modal2Ref = useRef<HTMLDivElement>( null )
515
643
  const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap()
@@ -533,6 +661,216 @@ const modal2OpenHandler = useCallback( () => {
533
661
 
534
662
  ---
535
663
 
664
+ ##### `useInView`
665
+
666
+ Check if the given target Element is intersecting with an ancestor Element or with a top-level document's viewport.
667
+
668
+ <details>
669
+
670
+ <summary style="cursor:pointer">Parameters</summary>
671
+
672
+ | Parameter | Type | Description |
673
+ |-----------|------|-------------|
674
+ | `target` | `React.RefObject<Element\|null>` | The React.RefObject of the target Element to observe. |
675
+ | `options` | `UseInViewOptions` | (Optional) An object defining custom `IntersectionObserver` options. |
676
+ | `options.root` | `Element\|Document\|false\|null` | (Optional) Identifies the `Element` or `Document` whose bounds are treated as the bounding box of the viewport for the Element which is the observer's target. |
677
+ | `options.margin` | `MarginType` | (Optional) A string, formatted similarly to the CSS margin property's value, which contains offsets for one or more sides of the root's bounding box. |
678
+ | `options.amount` | `'all'\|'some'\|number\|number[]` | (Optional) The intersecting target thresholds. |
679
+ | | | Threshold can be set to: |
680
+ | | | - `all` - `1` will be used. |
681
+ | | | - `some` - `0.5` will be used. |
682
+ | | | - `number` |
683
+ | | | - `number[]` |
684
+ | `options.once` | `boolean` | (Optional) By setting this to `true` the observer will be disconnected after the target Element enters the viewport. |
685
+ | `options.initial` | `boolean` | (Optional) Initial value. Default: `false`. |
686
+ | `options.enable` | `boolean` | (Optional) Defines the initial observation activity. Use the returned `setEnabled` to update this state. Default: `true`. |
687
+ | `options.onStart` | `OnStartHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
688
+ | | | This callback is awaited before any state update. |
689
+ | | | If an error is thrown the React State update won't be fired. |
690
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
691
+
692
+ </details>
693
+
694
+ ---
695
+
696
+ <details>
697
+
698
+ <summary style="cursor:pointer">Returns</summary>
699
+
700
+ Type: `UseInViewReturnType`
701
+
702
+ An object containing:
703
+
704
+ - `inView`: `boolean` - Indicates whether the target Element is in viewport or not.
705
+ - `setInView`: `React.Dispatch<React.SetStateAction<boolean>>` - A React Dispatch SetState action that allows custom state updates.
706
+ - `enabled`: `boolean` - Indicates whether the target Element is being observed or not.
707
+ - `setEnabled`: `React.Dispatch<React.SetStateAction<boolean>>` - A React Dispatch SetState action that allows to enable/disable observation when needed.
708
+ - `observer`: `IntersectionObserver | undefined` - The `IntersectionObserver` instance. It could be `undefined` if `IntersectionObserver` is not available or observation is not enabled.
709
+
710
+ </details>
711
+
712
+ ---
713
+
714
+ <details>
715
+
716
+ <summary style="cursor:pointer">Usage</summary>
717
+
718
+ ###### Basic usage
719
+
720
+ ```tsx
721
+ 'use client'
722
+
723
+ import { useRef } from 'react'
724
+ import { useInView } from '@alessiofrittoli/react-hooks'
725
+
726
+ const UseInViewExample: React.FC = () => {
727
+
728
+ const targetRef = useRef<HTMLDivElement>( null )
729
+ const { inView } = useInView( ref )
730
+
731
+ return (
732
+ Array.from( Array( 6 ) ).map( ( value, index ) => (
733
+ <div
734
+ key={ index }
735
+ style={ {
736
+ height : '50vh',
737
+ border : '1px solid red',
738
+ display : 'flex',
739
+ alignItems : 'center',
740
+ justifyContent : 'center',
741
+ } }
742
+ >
743
+ <div
744
+ ref={ index === 2 ? targetRef : undefined }
745
+ style={ {
746
+ width : 150,
747
+ height : 150,
748
+ borderRadius : 12,
749
+ display : 'flex',
750
+ alignItems : 'center',
751
+ justifyContent : 'center',
752
+ background : inView ? '#51AF83' : '#201A1B',
753
+ color : inView ? '#201A1B' : '#FFFFFF',
754
+ } }
755
+ >{ index + 1 }</div>
756
+ </div>
757
+ ) )
758
+ )
759
+
760
+ }
761
+ ```
762
+
763
+ ---
764
+
765
+ ###### Disconnect observer after target enters the viewport
766
+
767
+ ```tsx
768
+ 'use client'
769
+
770
+ import { useRef } from 'react'
771
+ import { useInView } from '@alessiofrittoli/react-hooks'
772
+
773
+ const OnceExample: React.FC = () => {
774
+
775
+ const targetRef = useRef<HTMLDivElement>( null )
776
+ const { inView } = useInView( targetRef, { once: true } )
777
+
778
+ useEffect( () => {
779
+
780
+ if ( ! inView ) return
781
+ console.count( 'Fired only once: element entered viewport.' )
782
+
783
+ }, [ inView ] )
784
+
785
+ return (
786
+ <div
787
+ ref={ targetRef }
788
+ style={ {
789
+ height : 200,
790
+ background : inView ? 'lime' : 'gray',
791
+ } }
792
+ />
793
+ )
794
+
795
+ }
796
+ ```
797
+
798
+ ---
799
+
800
+ ###### Observe target only when needed
801
+
802
+ ```tsx
803
+ 'use client'
804
+
805
+ import { useRef } from 'react'
806
+ import { useInView } from '@alessiofrittoli/react-hooks'
807
+
808
+ const OnDemandObservation: React.FC = () => {
809
+
810
+ const targetRef = useRef<HTMLDivElement>( null )
811
+ const {
812
+ inView, enabled, setEnabled
813
+ } = useInView( targetRef, { enable: false } )
814
+
815
+ return (
816
+ <div>
817
+ <button onClick={ () => setEnabled( prev => ! prev ) }>
818
+ { enabled ? 'Disconnect observer' : 'Observe' }
819
+ </button>
820
+ <div
821
+ ref={ targetRef }
822
+ style={ {
823
+ height : 200,
824
+ marginTop : 50,
825
+ background : inView ? 'lime' : 'gray',
826
+ } }
827
+ />
828
+ </div>
829
+ )
830
+
831
+ }
832
+ ```
833
+
834
+ ---
835
+
836
+ ###### Execute custom callback when intersection occurs
837
+
838
+ ```tsx
839
+ 'use client'
840
+
841
+ import { useRef } from 'react'
842
+ import { useInView, type OnStartHandler } from '@alessiofrittoli/react-hooks'
843
+
844
+
845
+ const AsyncStartExample: React.FC = () => {
846
+
847
+ const targetRef = useRef<HTMLDivElement>( null )
848
+ const onStart = useCallback<OnStartHandler>( async entry => {
849
+
850
+ console.log( 'Delaying state update...' )
851
+ await new Promise( resolve => setTimeout( resolve, 1000 ) ) // Simulate delay
852
+ console.log( 'Async task completed. `inView` will now be updated.' )
853
+
854
+ }, [] )
855
+
856
+ const { inView } = useInView( targetRef, { onStart } )
857
+
858
+ return (
859
+ <div
860
+ ref={ targetRef }
861
+ style={ {
862
+ height : 200,
863
+ background : inView ? 'lime' : 'gray',
864
+ } }
865
+ />
866
+ )
867
+ }
868
+ ```
869
+
870
+ </details>
871
+
872
+ ---
873
+
536
874
  #### Miscellaneous
537
875
 
538
876
  ##### `useIsClient`
@@ -558,20 +896,12 @@ Type: `boolean`
558
896
 
559
897
  <summary style="cursor:pointer">Usage</summary>
560
898
 
561
- ###### Importing the hook
562
-
563
- ```tsx
564
- import { useIsClient } from '@alessiofrittoli/react-hooks'
565
- // or
566
- import { useIsClient } from '@alessiofrittoli/react-hooks/misc'
567
- ```
568
-
569
899
  ###### Basic usage
570
900
 
571
901
  ```tsx
572
902
  'use client'
573
903
 
574
- import { useIsClient } from '@alessiofrittoli/react-hooks/misc'
904
+ import { useIsClient } from '@alessiofrittoli/react-hooks'
575
905
 
576
906
  export const ClientComponent: React.FC = () => {
577
907
 
@@ -611,20 +941,12 @@ Note that if the React Hook/Component has no state updates, `useIsFirstRender` w
611
941
 
612
942
  <summary style="cursor:pointer">Usage</summary>
613
943
 
614
- ###### Importing the hook
615
-
616
- ```tsx
617
- import { useIsFirstRender } from '@alessiofrittoli/react-hooks'
618
- // or
619
- import { useIsFirstRender } from '@alessiofrittoli/react-hooks/misc'
620
- ```
621
-
622
944
  ###### Basic usage
623
945
 
624
946
  ```tsx
625
947
  'use client'
626
948
 
627
- import { useIsFirstRender } from '@alessiofrittoli/react-hooks/misc'
949
+ import { useIsFirstRender } from '@alessiofrittoli/react-hooks'
628
950
 
629
951
  export const ClientComponent: React.FC = () => {
630
952
 
@@ -674,21 +996,13 @@ Modified version of `useEffect` that skips the first render.
674
996
 
675
997
  <summary style="cursor:pointer">Usage</summary>
676
998
 
677
- ###### Importing the hook
678
-
679
- ```tsx
680
- import { useUpdateEffect } from '@alessiofrittoli/react-hooks'
681
- // or
682
- import { useUpdateEffect } from '@alessiofrittoli/react-hooks/misc'
683
- ```
684
-
685
999
  ###### Basic usage
686
1000
 
687
1001
  ```tsx
688
1002
  'use client'
689
1003
 
690
1004
  import { useEffect, useState } from 'react'
691
- import { useUpdateEffect } from '@alessiofrittoli/react-hooks/misc'
1005
+ import { useUpdateEffect } from '@alessiofrittoli/react-hooks'
692
1006
 
693
1007
  export const ClientComponent: React.FC = () => {
694
1008
 
package/dist/eslint.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var n={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:"(useUpdateEffect)"}]}}]};exports.config = n;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var o=["useUpdateEffect"],n= exports.config ={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:o.join("|")}]}}]};exports.config = n;
package/dist/eslint.mjs CHANGED
@@ -1 +1 @@
1
- var o={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:"(useUpdateEffect)"}]}}]};export{o as config};
1
+ var o=["useUpdateEffect"],i={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:o.join("|")}]}}]};export{i as config};