@alessiofrittoli/react-hooks 3.3.0-alpha.2 → 3.3.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
@@ -29,19 +29,31 @@
29
29
  - [`useStorage`](#usestorage)
30
30
  - [`useLocalStorage`](#uselocalstorage)
31
31
  - [`useSessionStorage`](#usesessionstorage)
32
- - [`useMediaQuery`](#usemediaquery)
32
+ - [`useConnection`](#useconnection)
33
33
  - [`useDarkMode`](#usedarkmode)
34
+ - [`useEventListener`](#useeventlistener)
34
35
  - [`useIsPortrait`](#useisportrait)
36
+ - [`useMediaQuery`](#usemediaquery)
35
37
  - [DOM API](#dom-api)
36
- - [`useScrollBlock`](#usescrollblock)
37
38
  - [`useFocusTrap`](#usefocustrap)
38
39
  - [`useInView`](#useinview)
40
+ - [`useScrollBlock`](#usescrollblock)
39
41
  - [Miscellaneous](#miscellaneous)
42
+ - [`useInput`](#useinput)
40
43
  - [`useDeferCallback`](#usedefercallback)
44
+ - [`useEffectOnce`](#useeffectonce)
45
+ - [`useUpdateEffect`](#useupdateeffect)
41
46
  - [`useIsClient`](#useisclient)
42
47
  - [`useIsFirstRender`](#useisfirstrender)
43
- - [`useUpdateEffect`](#useupdateeffect)
44
48
  - [`usePagination`](#usepagination)
49
+ - [`useSelection`](#useselection)
50
+ - [Timers](#timers)
51
+ - [`useDebounce`](#usedebounce)
52
+ - [`useInterval`](#useinterval)
53
+ - [`useIntervalWhenVisible`](#useintervalwhenvisible)
54
+ - [`useLightInterval`](#uselightinterval)
55
+ - [`useTimeout`](#usetimeout)
56
+ - [`useLightTimeout`](#uselighttimeout)
45
57
  - [Development](#development)
46
58
  - [Install depenendencies](#install-depenendencies)
47
59
  - [Build the source code](#build-the-source-code)
@@ -95,6 +107,7 @@ export default config
95
107
  #### Updates in the latest release 🎉
96
108
 
97
109
  - Add `useDeferCallback`. See [API Reference](#usedefercallback) for more info.
110
+ - Add missing API Referefence sections.
98
111
 
99
112
  ---
100
113
 
@@ -269,75 +282,26 @@ Applies the same API Reference.
269
282
 
270
283
  ---
271
284
 
272
- ##### `useMediaQuery`
273
-
274
- Get Document Media matches and listen for changes.
275
-
276
- <details>
277
-
278
- <summary style="cursor:pointer">Parameters</summary>
279
-
280
- | Parameter | Type | Default | Description |
281
- |-----------|----------|---------|-------------|
282
- | `query` | `string` | - | A string specifying the media query to parse into a `MediaQueryList`. |
283
- | `options` | `UseMediaQueryOptions\|UseMediaQueryStateOptions` | - | An object defining custom options. |
284
- | `options.updateState` | `boolean` | `true` | Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched. |
285
- | `options.onChange` | `OnChangeHandler` | - | A custom callback that will be invoked on initial page load and when the given `query` change event get dispatched. |
286
- | | | | This callback is required if `updateState` is set to `false`. |
287
-
288
- </details>
285
+ ##### `useConnection`
289
286
 
290
- ---
287
+ Get states about Internet Connection.
291
288
 
292
289
  <details>
293
290
 
294
291
  <summary style="cursor:pointer">Returns</summary>
295
292
 
296
- Type: `boolean|void`
297
-
298
- - `true` or `false` if the document currently matches the media query list or not.
299
- - `void` if `updateState` is set to `false`.
300
-
301
- </details>
302
-
303
- ---
304
-
305
- <details>
306
-
307
- <summary style="cursor:pointer">Usage</summary>
308
-
309
- ###### Check if user device prefers dark color scheme
310
-
311
- ```tsx
312
- import { useMediaQuery } from '@alessiofrittoli/react-hooks'
313
-
314
- const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
315
- ```
316
-
317
- ---
318
-
319
- ###### Listen changes with no state updates
293
+ Type: `UseConnectionReturnType`
320
294
 
321
- ```tsx
322
- import { useMediaQuery } from '@alessiofrittoli/react-hooks'
295
+ An object with the following properties:
323
296
 
324
- useMediaQuery( '(prefers-color-scheme: dark)', {
325
- updateState: false,
326
- onChange( matches ) {
327
- console.log( 'is dark OS?', matches )
328
- }
329
- } )
330
- ```
297
+ - `connection`: `online | offline` - Indicates the connections status.
298
+ - `isOnline`: `boolean` - Indicates whether the current device is online.
299
+ - `isOffline`: `boolean` - Indicates whether the current device is offline.
331
300
 
332
301
  </details>
333
302
 
334
303
  ---
335
304
 
336
- ##### `useConnection`
337
-
338
- Docs coming soon
339
-
340
- ---
341
305
 
342
306
  ##### `useDarkMode`
343
307
 
@@ -492,20 +456,26 @@ Just make sure to define both `light` and `dark` theme-color tags in your docume
492
456
 
493
457
  ---
494
458
 
495
- ##### `useIsPortrait`
496
-
497
- Check if device is portrait oriented.
459
+ ##### `useEventListener`
498
460
 
499
- React State get updated when device orientation changes.
461
+ Attach a new Event listener to the `Window`, `Document`, `MediaQueryList` or an `HTMLElement`.
500
462
 
501
463
  <details>
502
464
 
503
- <summary style="cursor:pointer">Returns</summary>
465
+ <summary style="cursor:pointer">Parameters</summary>
504
466
 
505
- Type: `boolean`
467
+ <details>
506
468
 
507
- - `true` if the device is portrait oriented.
508
- - `false` otherwise.
469
+ <summary style="cursor:pointer">Window events</summary>
470
+
471
+ | Parameter | Type | Description |
472
+ |-----------|----------|-------------|
473
+ | `type` | `K\|K[]` | The `Window` event name or an array of event names. |
474
+ | `options` | `WindowListenerOptions<K>` | An object defining init options. |
475
+ | `options.listener` | `WindowEventListener<K>` | The Window Event listener. |
476
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
477
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
478
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
509
479
 
510
480
  </details>
511
481
 
@@ -513,33 +483,53 @@ Type: `boolean`
513
483
 
514
484
  <details>
515
485
 
516
- <summary style="cursor:pointer">Usage</summary>
517
-
518
- ###### Check if user device is in landscape
519
-
520
- ```tsx
521
- import { useIsPortrait } from '@alessiofrittoli/react-hooks'
486
+ <summary style="cursor:pointer">Document events</summary>
522
487
 
523
- const isLandscape = ! useIsPortrait()
524
- ```
488
+ | Parameter | Type | Description |
489
+ |-----------|----------|-------------|
490
+ | `type` | `K\|K[]` | The `Document` event name or an array of event names. |
491
+ | `options` | `DocumentListenerOptions<K>` | An object defining init options. |
492
+ | `options.target` | `Document\|null\|React.RefObject<Document\|null>` | The `Document` reference or a React RefObject of the `Document`. |
493
+ | `options.listener` | `DocumentEventListener<K>` | The Document Event listener. |
494
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
495
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
496
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
525
497
 
526
498
  </details>
527
499
 
528
500
  ---
529
501
 
530
- #### DOM API
502
+ <details>
531
503
 
532
- ##### `useScrollBlock`
504
+ <summary style="cursor:pointer">HTMLElement events</summary>
533
505
 
534
- Prevent Element overflow.
506
+ | Parameter | Type | Description |
507
+ |-----------|----------|-------------|
508
+ | `type` | `K\|K[]` | The `HTMLElement` event name or an array of event names. |
509
+ | `options` | `ElementListenerOptions<K>` | An object defining init options. |
510
+ | `options.target` | `T\|React.RefObject<T\| null>` | The React RefObject of the target where the listener get attached to. |
511
+ | `options.listener` | `ElementEventListener<K>` | The HTMLElement Event listener. |
512
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
513
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
514
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
515
+
516
+ </details>
517
+
518
+ ---
535
519
 
536
520
  <details>
537
521
 
538
- <summary style="cursor:pointer">Parameters</summary>
522
+ <summary style="cursor:pointer">MediaQuery events</summary>
539
523
 
540
- | Parameter | Type | Default | Description |
541
- |-----------|------|---------|-------------|
542
- | `target` | `React.RefObject<HTMLElement\|null>` | `Document.documentElement` | (Optional) The React RefObject target HTMLElement. |
524
+ | Parameter | Type | Description |
525
+ |-----------|----------|-------------|
526
+ | `type` | `change` | The `MediaQueryList` event name. |
527
+ | `options` | `MediaQueryListenerOptions` | An object defining init options. |
528
+ | `options.query` | `string` | The Media Query string to check. |
529
+ | `options.listener` | `MediaQueryChangeListener` | The MediaQueryList Event listener. |
530
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
531
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
532
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
543
533
 
544
534
  </details>
545
535
 
@@ -547,11 +537,19 @@ Prevent Element overflow.
547
537
 
548
538
  <details>
549
539
 
550
- <summary style="cursor:pointer">Returns</summary>
540
+ <summary style="cursor:pointer">Custom events</summary>
551
541
 
552
- Type: `[ () => void, () => void ]`
542
+ | Parameter | Type | Description |
543
+ |-----------|----------|-------------|
544
+ | `type` | `K\|K[]` | The custom event name or an array of event names. |
545
+ | `options` | `CustomEventListenerOptions<T, K>` | An object defining init options. |
546
+ | `options.target` | `Document\|HTMLElement\|null\|React.RefObject<Document\|HTMLElement\|null>` | (Optional) The target where the listener get attached to. If not set, the listener will get attached to the `Window` object. |
547
+ | `options.listener` | `( event: T[ K ] ) => void` | The Event listener. |
548
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
549
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
550
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
553
551
 
554
- A tuple with block and restore scroll callbacks.
552
+ </details>
555
553
 
556
554
  </details>
557
555
 
@@ -561,176 +559,165 @@ A tuple with block and restore scroll callbacks.
561
559
 
562
560
  <summary style="cursor:pointer">Usage</summary>
563
561
 
564
- ###### Block Document Overflow
562
+ ###### Attach listeners to the Window object
565
563
 
566
564
  ```tsx
567
- import { useScrollBlock } from '@alessiofrittoli/react-hooks'
565
+ 'use client'
568
566
 
569
- const [ blockScroll, restoreScroll ] = useScrollBlock()
567
+ import { useCallback } from 'react'
568
+ import { useEventListener } from '@alessiofrittoli/react-hooks'
570
569
 
571
- const openPopUpHandler = useCallback( () => {
572
- ...
573
- blockScroll()
574
- }, [ blockScroll ] )
570
+ export const MyComponent: React.FC = () => {
575
571
 
576
- const closePopUpHandler = useCallback( () => {
577
- ...
578
- restoreScroll()
579
- }, [ restoreScroll ] )
572
+ useEventListener( 'popstate', {
573
+ listener: useCallback( event => {
574
+ ...
575
+ }, [] ),
576
+ } )
580
577
 
581
- ...
578
+ }
582
579
  ```
583
580
 
584
581
  ---
585
582
 
586
- ###### Block HTML Element Overflow
583
+ ###### Attach listeners to the Document object
587
584
 
588
585
  ```tsx
589
- const elementRef = useRef<HTMLDivElement>( null )
586
+ 'use client'
590
587
 
591
- const [ blockScroll, restoreScroll ] = useScrollBlock( elementRef )
588
+ import { useCallback } from 'react'
589
+ import { useEventListener } from '@alessiofrittoli/react-hooks'
592
590
 
593
- const scrollBlockHandler = useCallback( () => {
594
- ...
595
- blockScroll()
596
- }, [ blockScroll ] )
591
+ export const MyComponent: React.FC = () => {
597
592
 
598
- const scrollRestoreHandler = useCallback( () => {
599
- ...
600
- restoreScroll()
601
- }, [ restoreScroll ] )
593
+ useEventListener( 'click', {
594
+ target : typeof document !== 'undefined' ? document : null,
595
+ listener : useCallback( event => {
596
+ ...
597
+ }, [] ),
598
+ } )
602
599
 
603
- ...
600
+ }
604
601
  ```
605
602
 
606
- </details>
607
-
608
603
  ---
609
604
 
610
- ##### `useFocusTrap`
605
+ ###### Attach listeners to an HTMLElement
611
606
 
612
- Trap focus inside the given HTML Element.
607
+ ```tsx
608
+ 'use client'
613
609
 
614
- This comes pretty handy when rendering a modal that shouldn't be closed without a user required action.
610
+ import { useCallback, useRef } from 'react'
611
+ import { useEventListener } from '@alessiofrittoli/react-hooks'
615
612
 
616
- <details>
613
+ export const MyComponent: React.FC = () => {
617
614
 
618
- <summary style="cursor:pointer">Parameters</summary>
615
+ const buttonRef = useRef<HTMLButtonElement>( null )
619
616
 
620
- | Parameter | Type | Description |
621
- |-----------|------|-------------|
622
- | `target` | `React.RefObject<HTMLElement\|null>` | The target HTMLElement React RefObject to trap focus within. |
623
- | | | If no target is given, you must provide the target HTMLElement when calling `setFocusTrap`. |
617
+ useEventListener( 'click', {
618
+ target: buttonRef,
619
+ listener: useCallback( event => {
620
+ ...
621
+ }, [] ),
622
+ } )
624
623
 
625
- </details>
624
+ return (
625
+ <button ref={ buttonRef }>Button</button>
626
+ )
626
627
 
627
- ---
628
+ }
629
+ ```
628
630
 
629
- <details>
631
+ ---
630
632
 
631
- <summary style="cursor:pointer">Returns</summary>
633
+ ###### Attach listeners to a MediaQueryList
632
634
 
633
- Type: `readonly [ SetFocusTrap, RestoreFocusTrap ]`
635
+ ```tsx
636
+ import { useCallback } from 'react'
637
+ import { useEventListener } from '@alessiofrittoli/react-hooks'
634
638
 
635
- A tuple containing:
639
+ export const MyComponent: React.FC = () => {
636
640
 
637
- - `setFocusTrap`: A function to enable the focus trap. Optionally accept an HTMLElement as target.
638
- - `restoreFocusTrap`: A function to restore the previous focus state.
641
+ useEventListener( 'change', {
642
+ query : '(max-width: 768px)',
643
+ listener : useCallback( event => {
644
+ if ( event.matches ) {
645
+ ...
646
+ }
647
+ }, [] )
648
+ } )
639
649
 
640
- </details>
650
+ }
651
+ ```
641
652
 
642
653
  ---
643
654
 
644
- <details>
655
+ ###### Listen dispatched custom events
645
656
 
646
- <summary style="cursor:pointer">Usage</summary>
657
+ ```tsx
658
+ import { useCallback } from 'react'
659
+ import { useEventListener } from '@alessiofrittoli/react-hooks'
647
660
 
648
- ###### Defining the target on hook initialization
661
+ class CustomEvent extends Event
662
+ {
663
+ isCustom: boolean
649
664
 
650
- ```tsx
651
- import { useFocusTrap } from '@alessiofrittoli/react-hooks'
665
+ constructor( type: string, eventInitDict?: EventInit )
666
+ {
667
+ super( type, eventInitDict )
668
+ this.isCustom = true
669
+ }
670
+ }
652
671
 
653
- const modalRef = useRef<HTMLDivElement>( null )
654
- const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap( modalRef )
655
672
 
656
- const modalOpenHandler = useCallback( () => {
657
- if ( ! modalRef.current ) return
658
- // ... open modal
659
- setFocusTrap()
660
- modalRef.current.focus() // focus the dialog so next tab will focus the next element inside the modal
661
- }, [ setFocusTrap ] )
673
+ type CustomEventMap = {
674
+ customEventName: CustomEvent
675
+ }
662
676
 
663
- const modalCloseHandler = useCallback( () => {
664
- // ... close modal
665
- restoreFocusTrap() // cancel focus trap and restore focus to the last active element before enablig the focus trap
666
- }, [ restoreFocusTrap ] )
667
- ```
668
677
 
669
- ---
678
+ export const MyComponent: React.FC = () => {
670
679
 
671
- ###### Defining the target ondemand
680
+ const clickHandler = useCallback( () => {
681
+ document.dispatchEvent( new CustomEvent( 'customEventName' ) )
682
+ }, [] )
672
683
 
673
- ```tsx
674
- import { useFocusTrap } from '@alessiofrittoli/react-hooks'
684
+ useEventListener<CustomEventMap>( 'customEventName', {
685
+ target : typeof document !== 'undefined' ? document : null,
686
+ listener : useCallback( event => {
687
+ if ( event.isCustom ) {
688
+ ...
689
+ }
690
+ }, [] )
691
+ } )
675
692
 
676
- const modalRef = useRef<HTMLDivElement>( null )
677
- const modal2Ref = useRef<HTMLDivElement>( null )
678
- const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap()
679
693
 
680
- const modalOpenHandler = useCallback( () => {
681
- if ( ! modalRef.current ) return
682
- // ... open modal
683
- setFocusTrap( modalRef.current )
684
- modalRef.current.focus()
685
- }, [ setFocusTrap ] )
694
+ return (
695
+ <button onClick={ clickHandler }>Click me to dispatch custom event</button>
696
+ )
686
697
 
687
- const modal2OpenHandler = useCallback( () => {
688
- if ( ! modal2Ref.current ) return
689
- // ... open modal
690
- setFocusTrap( modal2Ref.current )
691
- modal2Ref.current.focus()
692
- }, [ setFocusTrap ] )
698
+ }
693
699
  ```
694
700
 
701
+ ---
702
+
695
703
  </details>
696
704
 
697
705
  ---
698
706
 
699
- ##### `useInView`
707
+ ##### `useIsPortrait`
700
708
 
701
- Check if the given target Element is intersecting with an ancestor Element or with a top-level document's viewport.
709
+ Check if device is portrait oriented.
710
+
711
+ React State get updated when device orientation changes.
702
712
 
703
713
  <details>
704
714
 
705
- <summary style="cursor:pointer">Parameters</summary>
715
+ <summary style="cursor:pointer">Returns</summary>
706
716
 
707
- | Parameter | Type | Description |
708
- |-----------|------|-------------|
709
- | `target` | `React.RefObject<Element\|null>` | The React.RefObject of the target Element to observe. |
710
- | `options` | `UseInViewOptions` | (Optional) An object defining custom `IntersectionObserver` options. |
711
- | `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. |
712
- | `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. |
713
- | `options.amount` | `'all'\|'some'\|number\|number[]` | (Optional) The intersecting target thresholds. |
714
- | | | Threshold can be set to: |
715
- | | | - `all` - `1` will be used. |
716
- | | | - `some` - `0.5` will be used. |
717
- | | | - `number` |
718
- | | | - `number[]` |
719
- | `options.once` | `boolean` | (Optional) By setting this to `true` the observer will be disconnected after the target Element enters the viewport. |
720
- | `options.initial` | `boolean` | (Optional) Initial value. This value is used while server rendering then will be updated in the client based on target visibility. Default: `false`. |
721
- | `options.enable` | `boolean` | (Optional) Defines the initial observation activity. Use the returned `setEnabled` to update this state. Default: `true`. |
722
- | `options.onIntersect` | `OnIntersectStateHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
723
- | | | This callback is awaited before any state update. |
724
- | | | If an error is thrown the React State update won't be fired. |
725
- | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
726
- | `options.onEnter` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
727
- | | | This callback is awaited before any state update. |
728
- | | | If an error is thrown the React State update won't be fired. |
729
- | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
730
- | `options.onExit` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
731
- | | | This callback is awaited before any state update. |
732
- | | | If an error is thrown the React State update won't be fired. |
733
- | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
717
+ Type: `boolean`
718
+
719
+ - `true` if the device is portrait oriented.
720
+ - `false` otherwise.
734
721
 
735
722
  </details>
736
723
 
@@ -738,9 +725,220 @@ Check if the given target Element is intersecting with an ancestor Element or wi
738
725
 
739
726
  <details>
740
727
 
741
- <summary style="cursor:pointer">Returns</summary>
728
+ <summary style="cursor:pointer">Usage</summary>
742
729
 
743
- Type: `UseInViewReturnType`
730
+ ###### Check if user device is in landscape
731
+
732
+ ```tsx
733
+ import { useIsPortrait } from '@alessiofrittoli/react-hooks'
734
+
735
+ const isLandscape = ! useIsPortrait()
736
+ ```
737
+
738
+ </details>
739
+
740
+ ---
741
+
742
+ ##### `useMediaQuery`
743
+
744
+ Get Document Media matches and listen for changes.
745
+
746
+ <details>
747
+
748
+ <summary style="cursor:pointer">Parameters</summary>
749
+
750
+ | Parameter | Type | Default | Description |
751
+ |-----------|----------|---------|-------------|
752
+ | `query` | `string` | - | A string specifying the media query to parse into a `MediaQueryList`. |
753
+ | `options` | `UseMediaQueryOptions\|UseMediaQueryStateOptions` | - | An object defining custom options. |
754
+ | `options.updateState` | `boolean` | `true` | Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched. |
755
+ | `options.onChange` | `OnChangeHandler` | - | A custom callback that will be invoked on initial page load and when the given `query` change event get dispatched. |
756
+ | | | | This callback is required if `updateState` is set to `false`. |
757
+
758
+ </details>
759
+
760
+ ---
761
+
762
+ <details>
763
+
764
+ <summary style="cursor:pointer">Returns</summary>
765
+
766
+ Type: `boolean|void`
767
+
768
+ - `true` or `false` if the document currently matches the media query list or not.
769
+ - `void` if `updateState` is set to `false`.
770
+
771
+ </details>
772
+
773
+ ---
774
+
775
+ <details>
776
+
777
+ <summary style="cursor:pointer">Usage</summary>
778
+
779
+ ###### Check if user device prefers dark color scheme
780
+
781
+ ```tsx
782
+ import { useMediaQuery } from '@alessiofrittoli/react-hooks'
783
+
784
+ const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
785
+ ```
786
+
787
+ ---
788
+
789
+ ###### Listen changes with no state updates
790
+
791
+ ```tsx
792
+ import { useMediaQuery } from '@alessiofrittoli/react-hooks'
793
+
794
+ useMediaQuery( '(prefers-color-scheme: dark)', {
795
+ updateState: false,
796
+ onChange( matches ) {
797
+ console.log( 'is dark OS?', matches )
798
+ }
799
+ } )
800
+ ```
801
+
802
+ </details>
803
+
804
+ ---
805
+
806
+ #### DOM API
807
+
808
+ ##### `useFocusTrap`
809
+
810
+ Trap focus inside the given HTML Element.
811
+
812
+ This comes pretty handy when rendering a modal that shouldn't be closed without a user required action.
813
+
814
+ <details>
815
+
816
+ <summary style="cursor:pointer">Parameters</summary>
817
+
818
+ | Parameter | Type | Description |
819
+ |-----------|------|-------------|
820
+ | `target` | `React.RefObject<HTMLElement\|null>` | The target HTMLElement React RefObject to trap focus within. |
821
+ | | | If no target is given, you must provide the target HTMLElement when calling `setFocusTrap`. |
822
+
823
+ </details>
824
+
825
+ ---
826
+
827
+ <details>
828
+
829
+ <summary style="cursor:pointer">Returns</summary>
830
+
831
+ Type: `readonly [ SetFocusTrap, RestoreFocusTrap ]`
832
+
833
+ A tuple containing:
834
+
835
+ - `setFocusTrap`: A function to enable the focus trap. Optionally accept an HTMLElement as target.
836
+ - `restoreFocusTrap`: A function to restore the previous focus state.
837
+
838
+ </details>
839
+
840
+ ---
841
+
842
+ <details>
843
+
844
+ <summary style="cursor:pointer">Usage</summary>
845
+
846
+ ###### Defining the target on hook initialization
847
+
848
+ ```tsx
849
+ import { useFocusTrap } from '@alessiofrittoli/react-hooks'
850
+
851
+ const modalRef = useRef<HTMLDivElement>( null )
852
+ const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap( modalRef )
853
+
854
+ const modalOpenHandler = useCallback( () => {
855
+ if ( ! modalRef.current ) return
856
+ // ... open modal
857
+ setFocusTrap()
858
+ modalRef.current.focus() // focus the dialog so next tab will focus the next element inside the modal
859
+ }, [ setFocusTrap ] )
860
+
861
+ const modalCloseHandler = useCallback( () => {
862
+ // ... close modal
863
+ restoreFocusTrap() // cancel focus trap and restore focus to the last active element before enablig the focus trap
864
+ }, [ restoreFocusTrap ] )
865
+ ```
866
+
867
+ ---
868
+
869
+ ###### Defining the target ondemand
870
+
871
+ ```tsx
872
+ import { useFocusTrap } from '@alessiofrittoli/react-hooks'
873
+
874
+ const modalRef = useRef<HTMLDivElement>( null )
875
+ const modal2Ref = useRef<HTMLDivElement>( null )
876
+ const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap()
877
+
878
+ const modalOpenHandler = useCallback( () => {
879
+ if ( ! modalRef.current ) return
880
+ // ... open modal
881
+ setFocusTrap( modalRef.current )
882
+ modalRef.current.focus()
883
+ }, [ setFocusTrap ] )
884
+
885
+ const modal2OpenHandler = useCallback( () => {
886
+ if ( ! modal2Ref.current ) return
887
+ // ... open modal
888
+ setFocusTrap( modal2Ref.current )
889
+ modal2Ref.current.focus()
890
+ }, [ setFocusTrap ] )
891
+ ```
892
+
893
+ </details>
894
+
895
+ ---
896
+
897
+ ##### `useInView`
898
+
899
+ Check if the given target Element is intersecting with an ancestor Element or with a top-level document's viewport.
900
+
901
+ <details>
902
+
903
+ <summary style="cursor:pointer">Parameters</summary>
904
+
905
+ | Parameter | Type | Description |
906
+ |-----------|------|-------------|
907
+ | `target` | `React.RefObject<Element\|null>` | The React.RefObject of the target Element to observe. |
908
+ | `options` | `UseInViewOptions` | (Optional) An object defining custom `IntersectionObserver` options. |
909
+ | `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. |
910
+ | `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. |
911
+ | `options.amount` | `'all'\|'some'\|number\|number[]` | (Optional) The intersecting target thresholds. |
912
+ | | | Threshold can be set to: |
913
+ | | | - `all` - `1` will be used. |
914
+ | | | - `some` - `0.5` will be used. |
915
+ | | | - `number` |
916
+ | | | - `number[]` |
917
+ | `options.once` | `boolean` | (Optional) By setting this to `true` the observer will be disconnected after the target Element enters the viewport. |
918
+ | `options.initial` | `boolean` | (Optional) Initial value. This value is used while server rendering then will be updated in the client based on target visibility. Default: `false`. |
919
+ | `options.enable` | `boolean` | (Optional) Defines the initial observation activity. Use the returned `setEnabled` to update this state. Default: `true`. |
920
+ | `options.onIntersect` | `OnIntersectStateHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
921
+ | | | This callback is awaited before any state update. |
922
+ | | | If an error is thrown the React State update won't be fired. |
923
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
924
+ | `options.onEnter` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
925
+ | | | This callback is awaited before any state update. |
926
+ | | | If an error is thrown the React State update won't be fired. |
927
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
928
+ | `options.onExit` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
929
+ | | | This callback is awaited before any state update. |
930
+ | | | If an error is thrown the React State update won't be fired. |
931
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
932
+
933
+ </details>
934
+
935
+ ---
936
+
937
+ <details>
938
+
939
+ <summary style="cursor:pointer">Returns</summary>
940
+
941
+ Type: `UseInViewReturnType`
744
942
 
745
943
  An object containing:
746
944
 
@@ -956,45 +1154,17 @@ const AsyncStartExample: React.FC = () => {
956
1154
 
957
1155
  ---
958
1156
 
959
- #### Miscellaneous
960
-
961
- ---
962
-
963
- ##### `useInput`
964
-
965
- Docs coming soon
966
-
967
- ---
968
-
969
- ##### `useDeferCallback`
970
-
971
- `useDeferCallback` will return a memoized and deferred version of the callback that only changes if one of the `inputs` in the dependency list has changed.
972
-
973
- Since [`deferCallback`](https://npmjs.com/package/@alessiofrittoli/web-utils?activeTab=readme#deferCallback) returns a new function when called, it may cause your child components to uselessly re-validate when a state update occurs in the main component.
974
- To avoid these pitfalls you can memoize and defer your task with `useDeferCallback`.
975
-
976
- Take a look at [`deferTask`](https://npmjs.com/package/@alessiofrittoli/web-utils?activeTab=readme#deferTask) to defer single tasks in a function handler.
977
-
978
- <details>
979
-
980
- <summary style="cursor:pointer">Type Parameters</summary>
981
-
982
- | Parameter | Description |
983
- |-----------|------------------------------|
984
- | `T` | The task function definition. `unknown` types will be inherited by your function type definition. |
985
- | `U` | The task function arguments. `unknown` types will be inherited by your function type. |
986
-
987
- </details>
1157
+ ##### `useScrollBlock`
988
1158
 
989
- ---
1159
+ Prevent Element overflow.
990
1160
 
991
1161
  <details>
992
1162
 
993
1163
  <summary style="cursor:pointer">Parameters</summary>
994
1164
 
995
- | Parameter | Type | Description |
996
- |-----------|----------|-----------------------------|
997
- | `task` | `T` | The task callable function. |
1165
+ | Parameter | Type | Default | Description |
1166
+ |-----------|------|---------|-------------|
1167
+ | `target` | `React.RefObject<HTMLElement\|null>` | `Document.documentElement` | (Optional) The React RefObject target HTMLElement. |
998
1168
 
999
1169
  </details>
1000
1170
 
@@ -1004,9 +1174,9 @@ Take a look at [`deferTask`](https://npmjs.com/package/@alessiofrittoli/web-util
1004
1174
 
1005
1175
  <summary style="cursor:pointer">Returns</summary>
1006
1176
 
1007
- Type: `( ...args: U ) => Promise<Awaited<ReturnType<T>>>`
1177
+ Type: `[ () => void, () => void ]`
1008
1178
 
1009
- A new memoized handler which returns a new Promise that returns the `task` result once fulfilled.
1179
+ A tuple with block and restore scroll callbacks.
1010
1180
 
1011
1181
  </details>
1012
1182
 
@@ -1016,44 +1186,109 @@ A new memoized handler which returns a new Promise that returns the `task` resul
1016
1186
 
1017
1187
  <summary style="cursor:pointer">Usage</summary>
1018
1188
 
1189
+ ###### Block Document Overflow
1190
+
1019
1191
  ```tsx
1020
- const MyComponent: React.FC = () => {
1192
+ import { useScrollBlock } from '@alessiofrittoli/react-hooks'
1021
1193
 
1022
- const clickHandler = useDeferCallback<React.MouseEventHandler>(
1023
- event => { ... }, []
1024
- )
1194
+ const [ blockScroll, restoreScroll ] = useScrollBlock()
1025
1195
 
1026
- return (
1027
- <button onClick={ clickHandler }>Button</button>
1028
- )
1196
+ const openPopUpHandler = useCallback( () => {
1197
+ ...
1198
+ blockScroll()
1199
+ }, [ blockScroll ] )
1029
1200
 
1030
- }
1201
+ const closePopUpHandler = useCallback( () => {
1202
+ ...
1203
+ restoreScroll()
1204
+ }, [ restoreScroll ] )
1205
+
1206
+ ...
1207
+ ```
1208
+
1209
+ ---
1210
+
1211
+ ###### Block HTML Element Overflow
1212
+
1213
+ ```tsx
1214
+ const elementRef = useRef<HTMLDivElement>( null )
1215
+
1216
+ const [ blockScroll, restoreScroll ] = useScrollBlock( elementRef )
1217
+
1218
+ const scrollBlockHandler = useCallback( () => {
1219
+ ...
1220
+ blockScroll()
1221
+ }, [ blockScroll ] )
1222
+
1223
+ const scrollRestoreHandler = useCallback( () => {
1224
+ ...
1225
+ restoreScroll()
1226
+ }, [ restoreScroll ] )
1227
+
1228
+ ...
1031
1229
  ```
1032
1230
 
1033
1231
  </details>
1034
1232
 
1035
1233
  ---
1036
1234
 
1037
- ##### `useEffectOnce`
1235
+ #### Miscellaneous
1236
+
1237
+ ##### `useInput`
1238
+
1239
+ Handle input states with ease.
1240
+
1241
+ <details>
1242
+
1243
+ <summary style="cursor:pointer">Type Parameters</summary>
1244
+
1245
+ | Parameter | Description |
1246
+ |-----------|------------------------|
1247
+ | `I` | The input value type. |
1248
+ | `O` | The output value type. |
1038
1249
 
1039
- Docs coming soon
1250
+ </details>
1040
1251
 
1041
1252
  ---
1042
1253
 
1043
- ##### `useIsClient`
1254
+ <details>
1044
1255
 
1045
- Check if the React Hook or Component where this hook is executed is running in a browser environment.
1256
+ <summary style="cursor:pointer">Parameters</summary>
1046
1257
 
1047
- This is pretty usefull to avoid hydration errors.
1258
+ | Parameter | Type | Default | Description |
1259
+ |-----------|----------|---------|-----------------------------|
1260
+ | `options` | `UseInputOptions<I, O>` | `{}` | An object defining custom options. |
1261
+ | `options.inputRef` | `React.RefObject<InputType>` | - | (Optional) The React HTML input element ref. |
1262
+ | `options.initialValue` | `O\|null` | - | (Optional) The input initial value. |
1263
+ | `options.touchTimeout` | `number` | 600 | (Optional) A timeout in milliseconds which will be used to define the input as "touched" thus validations are triggered and errors can be displayed. |
1264
+ | `options.validate` | `ValidateValueHandler<O>` | - | (Optional) Value validation handler. If `parse` callback is given, the `value` will be parsed before validation. |
1265
+ | `options.parse` | `ParseValueHandler<I, O>` | - | (Optional) Parse value. |
1266
+ | `options.onChange` | `ChangeHandler<O>` | - | (Optional) A callable function executed when the `ChangeEvent` is dispatched on the HTML input element. |
1267
+
1268
+ </details>
1269
+
1270
+ ---
1048
1271
 
1049
1272
  <details>
1050
1273
 
1051
1274
  <summary style="cursor:pointer">Returns</summary>
1052
1275
 
1053
- Type: `boolean`
1276
+ Type: `UseInputOutput<I, O>`
1054
1277
 
1055
- - `true` if the React Hook or Component is running in a browser environment.
1056
- - `false` otherwise.
1278
+ An object containing the following properties:
1279
+
1280
+ | Property | Type | Description |
1281
+ |-----------------|------|-------------|
1282
+ | `isEmpty` | `boolean` | Indicates whether the Input is empty or not. |
1283
+ | `hasError` | `boolean` | Indicates whether the input has error or not. |
1284
+ | | | It will return true if the Input does not pass the validation checks and it has been touched. |
1285
+ | | | Please refer to the `isValid` property to check the Input validity regardless of whether it has been touched or not. |
1286
+ | `changeHandler` | `React.ChangeEventHandler<InputType>` | Change handler callback used to handle Input change events. |
1287
+ | `blurHandler` | `() => void` | Blur handler callback used to handle Input blur events. |
1288
+ | `setValue` | `( value: O ) => void` | Call `setValue` method to update input value. |
1289
+ | `submit` | `() => void` | Call `submit` method to re-run validations and ensure error state is updated successfully. |
1290
+ | `reset` | `() => void` | Call `reset` method to reset the Input state. |
1291
+ | `focus` | `() => void` | Call `focus` method to focus the Input Element. `inputRef` must be provided in the input options. |
1057
1292
 
1058
1293
  </details>
1059
1294
 
@@ -1066,16 +1301,93 @@ Type: `boolean`
1066
1301
  ###### Basic usage
1067
1302
 
1068
1303
  ```tsx
1069
- 'use client'
1304
+ const MyComponent: React.FC = () => {
1070
1305
 
1071
- import { useIsClient } from '@alessiofrittoli/react-hooks'
1306
+ const input = useInput<string>()
1072
1307
 
1073
- export const ClientComponent: React.FC = () => {
1308
+ return (
1309
+ <input
1310
+ type='text'
1311
+ value={ input.value || '' }
1312
+ onChange={ input.changeHandler }
1313
+ onBlur={ input.blurHandler }
1314
+ />
1315
+ )
1074
1316
 
1075
- const isClient = useIsClient()
1317
+ }
1318
+ ```
1319
+
1320
+ ---
1321
+
1322
+ ###### Displaying custom error messages
1323
+
1324
+ ```tsx
1325
+ import { useInput, type ValidateValueHandler } from '@alessiofrittoli/react-hooks'
1326
+
1327
+ const isNotEmpty: ValidateValueHandler<string> = value => (
1328
+ ! value ? false : value.trim().length > 0
1329
+ )
1330
+
1331
+ const MyComponent: React.FC = () => {
1332
+
1333
+ const input = useInput<string>( {
1334
+ validate: isNotEmpty,
1335
+ } )
1076
1336
 
1077
1337
  return (
1078
- <div>Running { ! isClient ? 'server' : 'client' }-side</div>
1338
+ <>
1339
+ <input
1340
+ value={ input.value || '' }
1341
+ onChange={ input.changeHandler }
1342
+ onBlur={ input.blurHandler }
1343
+ />
1344
+ { input.hasError && (
1345
+ <span>The input cannot be empty.</span>
1346
+ ) }
1347
+ </>
1348
+ )
1349
+
1350
+ }
1351
+ ```
1352
+
1353
+ ---
1354
+
1355
+ ###### Parsing and validating parsed value
1356
+
1357
+ ```tsx
1358
+ import { formatDate, isValidDate } from '@alessiofrittoli/date-utils'
1359
+ import { useInput, type ValidateValueHandler, type ParseValueHandler } from '@alessiofrittoli/react-hooks'
1360
+
1361
+ const parseStringToDate: ParseValueHandler<string, Date> = value => (
1362
+ value ? new Date( value ) : undefined
1363
+ )
1364
+
1365
+
1366
+ const validateInputDate: ValidateValueHandler<Date> = value => (
1367
+ isValidDate( value ) && value.getTime() > Date.now()
1368
+ )
1369
+
1370
+
1371
+ const MyComponent: React.FC = () => {
1372
+
1373
+ const input = useInput<string, Date>( {
1374
+ parse : parseStringToDate,
1375
+ validate : validateInputDate,
1376
+ } )
1377
+
1378
+
1379
+ return (
1380
+ <>
1381
+ <input
1382
+ type='datetime-local'
1383
+ value={ input.value ? formatDate( input.value, 'Y-m-dTH:i' ) : '' }
1384
+ onChange={ input.changeHandler }
1385
+ onBlur={ input.blurHandler }
1386
+ />
1387
+ { input.hasError && (
1388
+ <span>Please choose a date no earlier than today</span>
1389
+ ) }
1390
+ </>
1079
1391
  )
1080
1392
 
1081
1393
  }
@@ -1085,20 +1397,47 @@ export const ClientComponent: React.FC = () => {
1085
1397
 
1086
1398
  ---
1087
1399
 
1088
- ##### `useIsFirstRender`
1400
+ ##### `useDeferCallback`
1089
1401
 
1090
- Check if is first React Hook/Component render.
1402
+ `useDeferCallback` will return a memoized and deferred version of the callback that only changes if one of the `inputs` in the dependency list has changed.
1403
+
1404
+ Since [`deferCallback`](https://npmjs.com/package/@alessiofrittoli/web-utils?activeTab=readme#deferCallback) returns a new function when called, it may cause your child components to uselessly re-validate when a state update occurs in the main component.
1405
+ To avoid these pitfalls you can memoize and defer your task with `useDeferCallback`.
1406
+
1407
+ Take a look at [`deferTask`](https://npmjs.com/package/@alessiofrittoli/web-utils?activeTab=readme#deferTask) to defer single tasks in a function handler.
1091
1408
 
1092
1409
  <details>
1093
1410
 
1094
- <summary style="cursor:pointer">Returns</summary>
1411
+ <summary style="cursor:pointer">Type Parameters</summary>
1095
1412
 
1096
- Type: `boolean`
1413
+ | Parameter | Description |
1414
+ |-----------|------------------------------|
1415
+ | `T` | The task function definition. `unknown` types will be inherited by your function type definition. |
1416
+ | `U` | The task function arguments. `unknown` types will be inherited by your function type. |
1097
1417
 
1098
- - `true` at the mount time.
1099
- - `false` otherwise.
1418
+ </details>
1100
1419
 
1101
- Note that if the React Hook/Component has no state updates, `useIsFirstRender` will always return `true`.
1420
+ ---
1421
+
1422
+ <details>
1423
+
1424
+ <summary style="cursor:pointer">Parameters</summary>
1425
+
1426
+ | Parameter | Type | Description |
1427
+ |-----------|----------|-----------------------------|
1428
+ | `task` | `T` | The task callable function. |
1429
+
1430
+ </details>
1431
+
1432
+ ---
1433
+
1434
+ <details>
1435
+
1436
+ <summary style="cursor:pointer">Returns</summary>
1437
+
1438
+ Type: `( ...args: U ) => Promise<Awaited<ReturnType<T>>>`
1439
+
1440
+ A new memoized handler which returns a new Promise that returns the `task` result once fulfilled.
1102
1441
 
1103
1442
  </details>
1104
1443
 
@@ -1108,31 +1447,70 @@ Note that if the React Hook/Component has no state updates, `useIsFirstRender` w
1108
1447
 
1109
1448
  <summary style="cursor:pointer">Usage</summary>
1110
1449
 
1111
- ###### Basic usage
1450
+ ```tsx
1451
+ const MyComponent: React.FC = () => {
1452
+
1453
+ const clickHandler = useDeferCallback<React.MouseEventHandler>(
1454
+ event => { ... }, []
1455
+ )
1456
+
1457
+ return (
1458
+ <button onClick={ clickHandler }>Button</button>
1459
+ )
1460
+
1461
+ }
1462
+ ```
1463
+
1464
+ </details>
1465
+
1466
+ ---
1467
+
1468
+ ##### `useEffectOnce`
1469
+
1470
+ Modified version of `useEffect` that only run once on intial load.
1471
+
1472
+ <details>
1473
+
1474
+ <summary style="cursor:pointer">Parameters</summary>
1475
+
1476
+ | Parameter | Type | Description |
1477
+ |-----------|------------------------|-------------|
1478
+ | `effect` | `React.EffectCallback` | Imperative function that can return a cleanup function. |
1479
+
1480
+ </details>
1481
+
1482
+ ---
1483
+
1484
+ <details>
1485
+
1486
+ <summary style="cursor:pointer">Usage</summary>
1112
1487
 
1113
1488
  ```tsx
1114
1489
  'use client'
1115
1490
 
1116
- import { useIsFirstRender } from '@alessiofrittoli/react-hooks'
1491
+ import { useEffect, useState } from 'react'
1492
+ import { useEffectOnce } from '@alessiofrittoli/react-hooks'
1117
1493
 
1118
1494
  export const ClientComponent: React.FC = () => {
1119
1495
 
1120
- const isFirstRender = useIsFirstRender()
1121
- const [ counter, setCounter ] = useState( 0 )
1496
+ const [ count, setCount ] = useState( 0 )
1122
1497
 
1123
1498
  useEffect( () => {
1124
1499
  const intv = setInterval( () => {
1125
- setCounter( prev => prev + 1 )
1500
+ setCount( prev => prev + 1 ) // update state each 1s
1126
1501
  }, 1000 )
1127
1502
  return () => clearInterval( intv )
1128
1503
  }, [] )
1129
1504
 
1505
+ useEffectOnce( () => {
1506
+ console.log( 'Component did mount' )
1507
+ return () => {
1508
+ console.log( 'Component did unmount' )
1509
+ }
1510
+ } )
1511
+
1130
1512
  return (
1131
- <div>
1132
- { isFirstRender ? 'First render' : 'Subsequent render' }
1133
- <hr />
1134
- { counter }
1135
- </div>
1513
+ <div>{ count }</div>
1136
1514
  )
1137
1515
 
1138
1516
  }
@@ -1163,8 +1541,6 @@ Modified version of `useEffect` that skips the first render.
1163
1541
 
1164
1542
  <summary style="cursor:pointer">Usage</summary>
1165
1543
 
1166
- ###### Basic usage
1167
-
1168
1544
  ```tsx
1169
1545
  'use client'
1170
1546
 
@@ -1207,19 +1583,707 @@ export const ClientComponent: React.FC = () => {
1207
1583
 
1208
1584
  ---
1209
1585
 
1210
- ##### `usePagination`
1586
+ ##### `useIsClient`
1211
1587
 
1212
- Get pagination informations based on the given options.
1588
+ Check if the React Hook or Component where this hook is executed is running in a browser environment.
1213
1589
 
1214
- This hook memoize the returned result of the [`paginate`](https://github.com/alessiofrittoli/math-utils/blob/master/docs/helpers/README.md#paginate) function imported from [`@alessiofrittoli/math-utils`](https://npmjs.com/package/@alessiofrittoli/math-utils).
1590
+ This is pretty usefull to avoid hydration errors.
1215
1591
 
1216
- See [`paginate`](https://github.com/alessiofrittoli/math-utils/blob/master/docs/helpers/README.md#paginate) function Documentation for more information about it.
1592
+ <details>
1593
+
1594
+ <summary style="cursor:pointer">Returns</summary>
1595
+
1596
+ Type: `boolean`
1597
+
1598
+ - `true` if the React Hook or Component is running in a browser environment.
1599
+ - `false` otherwise.
1600
+
1601
+ </details>
1217
1602
 
1218
1603
  ---
1219
1604
 
1220
- ##### `useSelection`
1605
+ <details>
1606
+
1607
+ <summary style="cursor:pointer">Usage</summary>
1221
1608
 
1222
- Docs coming soon
1609
+ ```tsx
1610
+ 'use client'
1611
+
1612
+ import { useIsClient } from '@alessiofrittoli/react-hooks'
1613
+
1614
+ export const ClientComponent: React.FC = () => {
1615
+
1616
+ const isClient = useIsClient()
1617
+
1618
+ return (
1619
+ <div>Running { ! isClient ? 'server' : 'client' }-side</div>
1620
+ )
1621
+
1622
+ }
1623
+ ```
1624
+
1625
+ </details>
1626
+
1627
+ ---
1628
+
1629
+ ##### `useIsFirstRender`
1630
+
1631
+ Check if is first React Hook/Component render.
1632
+
1633
+ <details>
1634
+
1635
+ <summary style="cursor:pointer">Returns</summary>
1636
+
1637
+ Type: `boolean`
1638
+
1639
+ - `true` at the mount time.
1640
+ - `false` otherwise.
1641
+
1642
+ Note that if the React Hook/Component has no state updates, `useIsFirstRender` will always return `true`.
1643
+
1644
+ </details>
1645
+
1646
+ ---
1647
+
1648
+ <details>
1649
+
1650
+ <summary style="cursor:pointer">Usage</summary>
1651
+
1652
+ ```tsx
1653
+ 'use client'
1654
+
1655
+ import { useIsFirstRender } from '@alessiofrittoli/react-hooks'
1656
+
1657
+ export const ClientComponent: React.FC = () => {
1658
+
1659
+ const isFirstRender = useIsFirstRender()
1660
+ const [ counter, setCounter ] = useState( 0 )
1661
+
1662
+ useEffect( () => {
1663
+ const intv = setInterval( () => {
1664
+ setCounter( prev => prev + 1 )
1665
+ }, 1000 )
1666
+ return () => clearInterval( intv )
1667
+ }, [] )
1668
+
1669
+ return (
1670
+ <div>
1671
+ { isFirstRender ? 'First render' : 'Subsequent render' }
1672
+ <hr />
1673
+ { counter }
1674
+ </div>
1675
+ )
1676
+
1677
+ }
1678
+ ```
1679
+
1680
+ </details>
1681
+
1682
+ ---
1683
+
1684
+ ##### `usePagination`
1685
+
1686
+ Get pagination informations based on the given options.
1687
+
1688
+ This hook memoize the returned result of the [`paginate`](https://github.com/alessiofrittoli/math-utils/blob/master/docs/helpers/README.md#paginate) function imported from [`@alessiofrittoli/math-utils`](https://npmjs.com/package/@alessiofrittoli/math-utils).
1689
+
1690
+ See [`paginate`](https://github.com/alessiofrittoli/math-utils/blob/master/docs/helpers/README.md#paginate) function Documentation for more information about it.
1691
+
1692
+ ---
1693
+
1694
+ ##### `useSelection`
1695
+
1696
+ A React hook for managing selection states in an array.
1697
+
1698
+ Provides functionality for single and group selection, as well as resetting the selection.
1699
+
1700
+ <details>
1701
+
1702
+ <summary style="cursor:pointer">Type Parameters</summary>
1703
+
1704
+ | Parameter | Description |
1705
+ |-----------|------------------------------|
1706
+ | `V` | The type of the values in the `array`. |
1707
+
1708
+ </details>
1709
+
1710
+ ---
1711
+
1712
+ <details>
1713
+
1714
+ <summary style="cursor:pointer">Parameters</summary>
1715
+
1716
+ | Parameter | Type | Default | Description |
1717
+ |-----------|-------|---------|-----------------------------|
1718
+ | `array` | `V[]` | - | The array of items to manage selection for. |
1719
+ | `initial` | `V[]` | [] | The initial selection state. |
1720
+
1721
+ </details>
1722
+
1723
+ ---
1724
+
1725
+ <details>
1726
+
1727
+ <summary style="cursor:pointer">Returns</summary>
1728
+
1729
+ An object containing the selection state and handlers.
1730
+
1731
+ - `selection`: `V[]` - The current selected items.
1732
+ - `hasSelection`: `boolean` - Indicates whether `selection` is not empty. Short-hand for `selection.length > 0`.
1733
+ - `isSelected`: `IsSelectedHandler<V>` - Check if the given `entry` is in the selection.
1734
+ - `setSelection`: `SetSelectionHandler<V>` - A React Dispatch SetStateAction that allows custom selection update.
1735
+ - `select`: `SelectHandler<V>` - Update selection by adding a new `entry` or removing the given `entry` if already exists in the selection.
1736
+ - `groupSelect`: `GroupSelectHandler<V>` - Select all items from the given `array` starting from the first item in the selection up to the given `entry`.
1737
+ - `selectAll`: `SelectAllHandler` - Add all entries from the given `array` to the selection.
1738
+ - `resetSelection`: `ResetSelectionHandler` - Removes all entries from the selection.
1739
+
1740
+ </details>
1741
+
1742
+ ---
1743
+
1744
+ <details>
1745
+
1746
+ <summary style="cursor:pointer">Usage</summary>
1747
+
1748
+ ```tsx
1749
+ 'use client'
1750
+
1751
+ import { useCallback, useMemo } from 'react'
1752
+ import { useSelection } from '@alessiofrittoli/react-hooks'
1753
+
1754
+ interface Item
1755
+ {
1756
+ id : number
1757
+ name : string
1758
+ }
1759
+
1760
+ const items: Item[] = [
1761
+ {
1762
+ id : 1,
1763
+ name : 'item-1',
1764
+ },
1765
+ {
1766
+ id : 2,
1767
+ name : 'item-2',
1768
+ },
1769
+ {
1770
+ id : 3,
1771
+ name : 'item-3',
1772
+ },
1773
+ {
1774
+ id : 4,
1775
+ name : 'item-4',
1776
+ },
1777
+ {
1778
+ id : 5,
1779
+ name : 'item-5',
1780
+ },
1781
+ ]
1782
+
1783
+
1784
+ const MyComponent: React.FC = () => {
1785
+
1786
+ const {
1787
+ setSelection, select, groupSelect, isSelected
1788
+ } = useSelection( useMemo( () => items.map( item => item.id ), [] ) )
1789
+
1790
+ const clickHandler = useCallback( ( id: Item[ 'id' ] ) => (
1791
+ ( event: React.MouseEvent<HTMLButtonElement> ) => {
1792
+ if ( event.shiftKey ) {
1793
+ return groupSelect( id ) // group select
1794
+ }
1795
+ if ( event.metaKey || event.ctrlKey ) {
1796
+ return select( id ) // toggle single item in selection
1797
+ }
1798
+ setSelection( prev => (
1799
+ prev.includes( id ) ? [] : [ id ] // toggle single item selection
1800
+ ) )
1801
+ }
1802
+ ), [ select, groupSelect, setSelection ] )
1803
+
1804
+ return (
1805
+ <ul>
1806
+ { items.map( item => (
1807
+ <li key={ item.id }>
1808
+ <button
1809
+ onClick={ clickHandler( item.id ) }
1810
+ style={ {
1811
+ border: isSelected( item.id ) ? '1px solid red' : ' 1px solid black'
1812
+ } }
1813
+ >{ item.name }</button>
1814
+ </li>
1815
+ ) ) }
1816
+ </ul>
1817
+ )
1818
+
1819
+ }
1820
+ ```
1821
+
1822
+ </details>
1823
+
1824
+ ---
1825
+
1826
+ #### Timers
1827
+
1828
+ #### `useDebounce`
1829
+
1830
+ Debounce a value by a specified delay.
1831
+
1832
+ This hook returns a debounced version of the input value, which only updates
1833
+ after the specified delay has passed without any changes to the input value.
1834
+
1835
+ It is useful for scenarios like search input fields or other cases where
1836
+ frequent updates should be minimized.
1837
+
1838
+ The `Timeout` automatically restarts when the given `value` changes.
1839
+
1840
+ <details>
1841
+
1842
+ <summary style="cursor:pointer">Type Parameters</summary>
1843
+
1844
+ | Parameter | Description |
1845
+ |-----------|--------------------------|
1846
+ | `T` | The type of the `value`. |
1847
+
1848
+ </details>
1849
+
1850
+ ---
1851
+
1852
+ <details>
1853
+
1854
+ <summary style="cursor:pointer">Parameters</summary>
1855
+
1856
+ | Parameter | Type | Default | Description |
1857
+ |-----------|----------|---------|-----------------------------|
1858
+ | `value` | `T` | - | The value to debounce. This can be of any type. |
1859
+ | `delay` | `number` | 500 | The debounce delay in milliseconds. |
1860
+
1861
+ </details>
1862
+
1863
+ ---
1864
+
1865
+ <details>
1866
+
1867
+ <summary style="cursor:pointer">Returns</summary>
1868
+
1869
+ Type: `T`
1870
+
1871
+ The debounced value, which updates only after the delay has passed.
1872
+
1873
+ </details>
1874
+
1875
+ ---
1876
+
1877
+ <details>
1878
+
1879
+ <summary style="cursor:pointer">Usage</summary>
1880
+
1881
+ ```tsx
1882
+ 'use client'
1883
+
1884
+ import { useEffect, useState } from 'react'
1885
+ import { useDebounce } from '@alessiofrittoli/react-hooks'
1886
+
1887
+ const MyComponent: React.FC = () => {
1888
+
1889
+ const [ query, setQuery ] = useState( '' )
1890
+ const debouncedQuery = useDebounce( query )
1891
+
1892
+ useEffect( () => {
1893
+ if ( ! debouncedQuery ) return
1894
+
1895
+ fetch( '...', {
1896
+ // ...
1897
+ body: JSON.stringify( { query: debouncedQuery } )
1898
+ } )
1899
+
1900
+ }, [ debouncedQuery ] )
1901
+
1902
+ return (
1903
+ <input
1904
+ onChange={ event => setQuery( event.target.value ) }
1905
+ />
1906
+ )
1907
+
1908
+ }
1909
+ ```
1910
+
1911
+ </details>
1912
+
1913
+ ---
1914
+
1915
+ #### `useInterval`
1916
+
1917
+ Schedules repeated execution of `callback` every `delay` milliseconds.
1918
+
1919
+ When `delay` is larger than `2147483647` or less than `1` or `NaN`, the `delay` will be set to `1`. Non-integer delays are truncated to an integer.
1920
+ If `callback` is not a function, a `TypeError` will be thrown.
1921
+
1922
+ The `Timeout` is automatically cancelled on unmount.
1923
+
1924
+ <details>
1925
+
1926
+ <summary style="cursor:pointer">Type Parameters</summary>
1927
+
1928
+ | Parameter | Description |
1929
+ |-----------|--------------------------|
1930
+ | `T` | An Array defining optional arguments passed to the `callback`. |
1931
+
1932
+ </details>
1933
+
1934
+ ---
1935
+
1936
+ <details>
1937
+
1938
+ <summary style="cursor:pointer">Parameters</summary>
1939
+
1940
+ | Parameter | Type | Default | Description |
1941
+ |-----------|----------|---------|-----------------------------|
1942
+ | `callback`| `TimerHandler<T>` | - | The function to call when the timer elapses. |
1943
+ | `options` | `TimerOptions<T>` | - | (Optional) An object defining custom timer options. |
1944
+ | `options.delay` | `number` | `1` | The number of milliseconds to wait before calling the `callback`. |
1945
+ | `options.args` | `T` | - | Optional arguments to pass when the `callback` is called. |
1946
+ | `options.autoplay` | `boolean` | `true` | Indicates whether auto start the timer. |
1947
+ | `options.updateState` | `boolean` | `false` | Whether to update React state about Timer running status. |
1948
+ | `options.runOnStart` | `boolean` | `false` | Indicates whether to execute the callback when timer starts. |
1949
+
1950
+ </details>
1951
+
1952
+ ---
1953
+
1954
+ <details>
1955
+
1956
+ <summary style="cursor:pointer">Returns</summary>
1957
+
1958
+ Type: `TimerReturnType | StateTimerReturnType`
1959
+
1960
+ An object with timer utilities.
1961
+
1962
+ - start: `StartTimer` - Manually start the timer.
1963
+ - stop: `StopTimer` - Manually stop the timer.
1964
+
1965
+ If `updateState` is set to `true` then the following property is added in the returned object.
1966
+
1967
+ - isActive: `boolean` - Indicates whether the timer is active.
1968
+
1969
+ </details>
1970
+
1971
+ ---
1972
+
1973
+ <details>
1974
+
1975
+ <summary style="cursor:pointer">Usage</summary>
1976
+
1977
+ ##### Basic usage
1978
+
1979
+ ```tsx
1980
+ 'use client'
1981
+
1982
+ import { useCallback } from 'react'
1983
+ import { useInterval } from '@alessiofrittoli/react-hooks'
1984
+
1985
+ const MyComponent: React.FC = () => {
1986
+
1987
+ const { stop } = useInterval( useCallback( () => {
1988
+ console.log( 'tick timer' )
1989
+ }, [] ), { delay: 1000 } )
1990
+
1991
+ return (
1992
+ <button onClick={ stop }>Stop timer</button>
1993
+ )
1994
+
1995
+ }
1996
+ ```
1997
+
1998
+ ---
1999
+
2000
+ ##### Rely on state updates
2001
+
2002
+ ```tsx
2003
+ 'use client'
2004
+
2005
+ import { useCallback } from 'react'
2006
+ import { useInterval } from '@alessiofrittoli/react-hooks'
2007
+
2008
+ const MyComponent: React.FC = () => {
2009
+
2010
+ const { isActive, start, stop } = useInterval( useCallback( () => {
2011
+ console.log( 'tick timer' )
2012
+ }, [] ), {
2013
+ delay : 1000,
2014
+ autoplay : false,
2015
+ runOnStart : true,
2016
+ updateState : true,
2017
+ } )
2018
+
2019
+ return (
2020
+ <>
2021
+ { ! isActive && (
2022
+ <button onClick={ start }>Start timer</button>
2023
+ ) }
2024
+ { isActive && (
2025
+ <button onClick={ stop }>Stop timer</button>
2026
+ ) }
2027
+ </>
2028
+ )
2029
+
2030
+ }
2031
+ ```
2032
+
2033
+ </details>
2034
+
2035
+ ---
2036
+
2037
+ #### `useIntervalWhenVisible`
2038
+
2039
+ Schedules repeated execution of `callback` every `delay` milliseconds when `Document` is visible.
2040
+
2041
+ This hook automatically starts and stops the interval based on the `Document` visibility.
2042
+
2043
+ This hook has the same API of [`useInterval`](#useinterval) and automatically starts and stops timers based on `Document` visibility.
2044
+ Refer to [`useInterval`](#useinterval) API Reference for more info.
2045
+
2046
+ ---
2047
+
2048
+ #### `useLightInterval`
2049
+
2050
+ Schedules repeated execution of `callback` every `delay` milliseconds.
2051
+
2052
+ This is a lighter version of [`useInterval`](#useinterval) and is suggested to use when a basic functionality is enough (no manual start/stop or state updates).
2053
+
2054
+ <details>
2055
+
2056
+ <summary style="cursor:pointer">Type Parameters</summary>
2057
+
2058
+ | Parameter | Description |
2059
+ |-----------|--------------------------|
2060
+ | `T` | An Array defining optional arguments passed to the `callback`. |
2061
+
2062
+ </details>
2063
+
2064
+ ---
2065
+
2066
+ <details>
2067
+
2068
+ <summary style="cursor:pointer">Parameters</summary>
2069
+
2070
+ | Parameter | Type | Default | Description |
2071
+ |-----------|----------|---------|-----------------------------|
2072
+ | `callback`| `TimerHandler<T>` | - | The function to call when the timer elapses. |
2073
+ | `options` | `BasicTimerOptions<T>` | - | (Optional) An object defining custom timer options. |
2074
+ | `options.delay` | `number` | `1` | The number of milliseconds to wait before calling the `callback`. |
2075
+ | `options.args` | `T` | - | Optional arguments to pass when the `callback` is called. |
2076
+
2077
+ </details>
2078
+
2079
+ ---
2080
+
2081
+ <details>
2082
+
2083
+ <summary style="cursor:pointer">Usage</summary>
2084
+
2085
+ ```tsx
2086
+ 'use client'
2087
+
2088
+ import { useCallback } from 'react'
2089
+ import { useLightInterval } from '@alessiofrittoli/react-hooks'
2090
+
2091
+ const MyComponent: React.FC = () => {
2092
+
2093
+ useLightInterval( useCallback( () => {
2094
+ console.log( 'tick timer' )
2095
+ }, [] ), { delay: 1000 } )
2096
+
2097
+ }
2098
+ ```
2099
+
2100
+ </details>
2101
+
2102
+ ---
2103
+
2104
+ #### `useTimeout`
2105
+
2106
+ Schedules execution of a one-time `callback` after `delay` milliseconds.
2107
+
2108
+ The `callback` will likely not be invoked in precisely `delay` milliseconds.
2109
+
2110
+ Node.js makes no guarantees about the exact timing of when callbacks will fire,
2111
+ nor of their ordering. The callback will be called as close as possible to the
2112
+ time specified.
2113
+
2114
+ When `delay` is larger than `2147483647` or less than `1` or `NaN`, the `delay`
2115
+ will be set to `1`. Non-integer delays are truncated to an integer.
2116
+
2117
+ If `callback` is not a function, a `TypeError` will be thrown.
2118
+
2119
+ The `Timeout` is automatically cancelled on unmount.
2120
+
2121
+ <details>
2122
+
2123
+ <summary style="cursor:pointer">Type Parameters</summary>
2124
+
2125
+ | Parameter | Description |
2126
+ |-----------|--------------------------|
2127
+ | `T` | An Array defining optional arguments passed to the `callback`. |
2128
+
2129
+ </details>
2130
+
2131
+ ---
2132
+
2133
+ <details>
2134
+
2135
+ <summary style="cursor:pointer">Parameters</summary>
2136
+
2137
+ | Parameter | Type | Default | Description |
2138
+ |-----------|----------|---------|-----------------------------|
2139
+ | `callback`| `TimerHandler<T>` | - | The function to call when the timer elapses. |
2140
+ | `options` | `TimerOptions<T>` | - | (Optional) An object defining custom timer options. |
2141
+ | `options.delay` | `number` | `1` | The number of milliseconds to wait before calling the `callback`. |
2142
+ | `options.args` | `T` | - | Optional arguments to pass when the `callback` is called. |
2143
+ | `options.autoplay` | `boolean` | `true` | Indicates whether auto start the timer. |
2144
+ | `options.updateState` | `boolean` | `false` | Whether to update React state about Timer running status. |
2145
+ | `options.runOnStart` | `boolean` | `false` | Indicates whether to execute the callback when timer starts. |
2146
+
2147
+ </details>
2148
+
2149
+ ---
2150
+
2151
+ <details>
2152
+
2153
+ <summary style="cursor:pointer">Returns</summary>
2154
+
2155
+ Type: `TimerReturnType | StateTimerReturnType`
2156
+
2157
+ An object with timer utilities.
2158
+
2159
+ - start: `StartTimer` - Manually start the timer.
2160
+ - stop: `StopTimer` - Manually stop the timer.
2161
+
2162
+ If `updateState` is set to `true` then the following property is added in the returned object.
2163
+
2164
+ - isActive: `boolean` - Indicates whether the timer is active.
2165
+
2166
+ </details>
2167
+
2168
+ ---
2169
+
2170
+ <details>
2171
+
2172
+ <summary style="cursor:pointer">Usage</summary>
2173
+
2174
+ ##### Basic usage
2175
+
2176
+ ```tsx
2177
+ 'use client'
2178
+
2179
+ import { useCallback } from 'react'
2180
+ import { useTimeout } from '@alessiofrittoli/react-hooks'
2181
+
2182
+ const MyComponent: React.FC = () => {
2183
+
2184
+ const { stop } = useTimeout( useCallback( () => {
2185
+ console.log( 'tick timer' )
2186
+ }, [] ), { delay: 1000 } )
2187
+
2188
+ return (
2189
+ <button onClick={ stop }>Stop timer</button>
2190
+ )
2191
+
2192
+ }
2193
+ ```
2194
+
2195
+ ---
2196
+
2197
+ ##### Rely on state updates
2198
+
2199
+ ```tsx
2200
+ 'use client'
2201
+
2202
+ import { useCallback } from 'react'
2203
+ import { useTimeout } from '@alessiofrittoli/react-hooks'
2204
+
2205
+ const MyComponent: React.FC = () => {
2206
+
2207
+ const { isActive, start, stop } = useTimeout( useCallback( () => {
2208
+ console.log( 'tick timer' )
2209
+ }, [] ), {
2210
+ delay : 1000,
2211
+ autoplay : false,
2212
+ runOnStart : true,
2213
+ updateState : true,
2214
+ } )
2215
+
2216
+ return (
2217
+ <>
2218
+ { ! isActive && (
2219
+ <button onClick={ start }>Start timer</button>
2220
+ ) }
2221
+ { isActive && (
2222
+ <button onClick={ stop }>Stop timer</button>
2223
+ ) }
2224
+ </>
2225
+ )
2226
+
2227
+ }
2228
+ ```
2229
+
2230
+ </details>
2231
+
2232
+ ---
2233
+
2234
+ #### `useLightTimeout`
2235
+
2236
+ Schedules execution of a one-time `callback` after `delay` milliseconds.
2237
+
2238
+ This is a lighter version of [`useTimeout`](#usetimeout) and is suggested to use when a basic functionality is enough (no manual start/stop or state updates).
2239
+
2240
+ <details>
2241
+
2242
+ <summary style="cursor:pointer">Type Parameters</summary>
2243
+
2244
+ | Parameter | Description |
2245
+ |-----------|--------------------------|
2246
+ | `T` | An Array defining optional arguments passed to the `callback`. |
2247
+
2248
+ </details>
2249
+
2250
+ ---
2251
+
2252
+ <details>
2253
+
2254
+ <summary style="cursor:pointer">Parameters</summary>
2255
+
2256
+ | Parameter | Type | Default | Description |
2257
+ |-----------|----------|---------|-----------------------------|
2258
+ | `callback`| `TimerHandler<T>` | - | The function to call when the timer elapses. |
2259
+ | `options` | `BasicTimerOptions<T>` | - | (Optional) An object defining custom timer options. |
2260
+ | `options.delay` | `number` | `1` | The number of milliseconds to wait before calling the `callback`. |
2261
+ | `options.args` | `T` | - | Optional arguments to pass when the `callback` is called. |
2262
+
2263
+ </details>
2264
+
2265
+ ---
2266
+
2267
+ <details>
2268
+
2269
+ <summary style="cursor:pointer">Usage</summary>
2270
+
2271
+ ```tsx
2272
+ 'use client'
2273
+
2274
+ import { useCallback } from 'react'
2275
+ import { useLightTimeout } from '@alessiofrittoli/react-hooks'
2276
+
2277
+ const MyComponent: React.FC = () => {
2278
+
2279
+ useLightTimeout( useCallback( () => {
2280
+ console.log( 'tick timer' )
2281
+ }, [] ), { delay: 1000 } )
2282
+
2283
+ }
2284
+ ```
2285
+
2286
+ </details>
1223
2287
 
1224
2288
  ---
1225
2289