@alessiofrittoli/react-hooks 1.1.0 → 2.0.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 +387 -73
- package/dist/eslint.js +1 -2
- package/dist/eslint.mjs +1 -2
- package/dist/index.d.mts +940 -11
- package/dist/index.d.ts +940 -11
- package/dist/index.js +1 -2
- package/dist/index.mjs +1 -2
- package/package.json +132 -145
- package/dist/browser-api/index.d.mts +0 -6
- package/dist/browser-api/index.d.ts +0 -6
- package/dist/browser-api/index.js +0 -2
- package/dist/browser-api/index.js.map +0 -1
- package/dist/browser-api/index.mjs +0 -2
- package/dist/browser-api/index.mjs.map +0 -1
- package/dist/browser-api/storage/index.d.mts +0 -4
- package/dist/browser-api/storage/index.d.ts +0 -4
- package/dist/browser-api/storage/index.js +0 -2
- package/dist/browser-api/storage/index.js.map +0 -1
- package/dist/browser-api/storage/index.mjs +0 -2
- package/dist/browser-api/storage/index.mjs.map +0 -1
- package/dist/browser-api/storage/useLocalStorage.d.mts +0 -11
- package/dist/browser-api/storage/useLocalStorage.d.ts +0 -11
- package/dist/browser-api/storage/useLocalStorage.js +0 -2
- package/dist/browser-api/storage/useLocalStorage.js.map +0 -1
- package/dist/browser-api/storage/useLocalStorage.mjs +0 -2
- package/dist/browser-api/storage/useLocalStorage.mjs.map +0 -1
- package/dist/browser-api/storage/useSessionStorage.d.mts +0 -11
- package/dist/browser-api/storage/useSessionStorage.d.ts +0 -11
- package/dist/browser-api/storage/useSessionStorage.js +0 -2
- package/dist/browser-api/storage/useSessionStorage.js.map +0 -1
- package/dist/browser-api/storage/useSessionStorage.mjs +0 -2
- package/dist/browser-api/storage/useSessionStorage.mjs.map +0 -1
- package/dist/browser-api/storage/useStorage.d.mts +0 -12
- package/dist/browser-api/storage/useStorage.d.ts +0 -12
- package/dist/browser-api/storage/useStorage.js +0 -2
- package/dist/browser-api/storage/useStorage.js.map +0 -1
- package/dist/browser-api/storage/useStorage.mjs +0 -2
- package/dist/browser-api/storage/useStorage.mjs.map +0 -1
- package/dist/browser-api/useIsPortrait.d.mts +0 -10
- package/dist/browser-api/useIsPortrait.d.ts +0 -10
- package/dist/browser-api/useIsPortrait.js +0 -2
- package/dist/browser-api/useIsPortrait.js.map +0 -1
- package/dist/browser-api/useIsPortrait.mjs +0 -2
- package/dist/browser-api/useIsPortrait.mjs.map +0 -1
- package/dist/browser-api/useMediaQuery.d.mts +0 -11
- package/dist/browser-api/useMediaQuery.d.ts +0 -11
- package/dist/browser-api/useMediaQuery.js +0 -2
- package/dist/browser-api/useMediaQuery.js.map +0 -1
- package/dist/browser-api/useMediaQuery.mjs +0 -2
- package/dist/browser-api/useMediaQuery.mjs.map +0 -1
- package/dist/dom-api/index.d.mts +0 -2
- package/dist/dom-api/index.d.ts +0 -2
- package/dist/dom-api/index.js +0 -2
- package/dist/dom-api/index.js.map +0 -1
- package/dist/dom-api/index.mjs +0 -2
- package/dist/dom-api/index.mjs.map +0 -1
- package/dist/dom-api/useFocusTrap.d.mts +0 -15
- package/dist/dom-api/useFocusTrap.d.ts +0 -15
- package/dist/dom-api/useFocusTrap.js +0 -2
- package/dist/dom-api/useFocusTrap.js.map +0 -1
- package/dist/dom-api/useFocusTrap.mjs +0 -2
- package/dist/dom-api/useFocusTrap.mjs.map +0 -1
- package/dist/dom-api/useScrollBlock.d.mts +0 -8
- package/dist/dom-api/useScrollBlock.d.ts +0 -8
- package/dist/dom-api/useScrollBlock.js +0 -2
- package/dist/dom-api/useScrollBlock.js.map +0 -1
- package/dist/dom-api/useScrollBlock.mjs +0 -2
- package/dist/dom-api/useScrollBlock.mjs.map +0 -1
- package/dist/eslint.js.map +0 -1
- package/dist/eslint.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/misc/index.d.mts +0 -3
- package/dist/misc/index.d.ts +0 -3
- package/dist/misc/index.js +0 -2
- package/dist/misc/index.js.map +0 -1
- package/dist/misc/index.mjs +0 -2
- package/dist/misc/index.mjs.map +0 -1
- package/dist/misc/useIsClient.d.mts +0 -8
- package/dist/misc/useIsClient.d.ts +0 -8
- package/dist/misc/useIsClient.js +0 -2
- package/dist/misc/useIsClient.js.map +0 -1
- package/dist/misc/useIsClient.mjs +0 -2
- package/dist/misc/useIsClient.mjs.map +0 -1
- package/dist/misc/useIsFirstRender.d.mts +0 -9
- package/dist/misc/useIsFirstRender.d.ts +0 -9
- package/dist/misc/useIsFirstRender.js +0 -2
- package/dist/misc/useIsFirstRender.js.map +0 -1
- package/dist/misc/useIsFirstRender.mjs +0 -2
- package/dist/misc/useIsFirstRender.mjs.map +0 -1
- package/dist/misc/usePagination.d.mts +0 -12
- package/dist/misc/usePagination.d.ts +0 -12
- package/dist/misc/usePagination.js +0 -2
- package/dist/misc/usePagination.js.map +0 -1
- package/dist/misc/usePagination.mjs +0 -2
- package/dist/misc/usePagination.mjs.map +0 -1
- package/dist/misc/useUpdateEffect.d.mts +0 -9
- package/dist/misc/useUpdateEffect.d.ts +0 -9
- package/dist/misc/useUpdateEffect.js +0 -2
- package/dist/misc/useUpdateEffect.js.map +0 -1
- package/dist/misc/useUpdateEffect.mjs +0 -2
- package/dist/misc/useUpdateEffect.mjs.map +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`
|
|
109
|
-
| `
|
|
110
|
-
| `type`
|
|
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
|
|
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
|
|
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
|
|
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
|
-
######
|
|
295
|
+
###### Check if user device prefers dark color scheme
|
|
291
296
|
|
|
292
297
|
```tsx
|
|
293
298
|
import { useMediaQuery } from '@alessiofrittoli/react-hooks'
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
|
|
300
|
+
const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
|
|
296
301
|
```
|
|
297
302
|
|
|
303
|
+
</details>
|
|
304
|
+
|
|
298
305
|
---
|
|
299
306
|
|
|
300
|
-
|
|
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
|
-
|
|
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
|
-
######
|
|
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
|
-
######
|
|
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
|
-
######
|
|
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
|
|
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
|
|
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
|
|
1005
|
+
import { useUpdateEffect } from '@alessiofrittoli/react-hooks'
|
|
692
1006
|
|
|
693
1007
|
export const ClientComponent: React.FC = () => {
|
|
694
1008
|
|
package/dist/eslint.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//# sourceMappingURL=eslint.js.map
|
|
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,2 +1 @@
|
|
|
1
|
-
var o={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:"
|
|
2
|
-
//# sourceMappingURL=eslint.mjs.map
|
|
1
|
+
var o=["useUpdateEffect"],i={recommended:[{rules:{"react-hooks/exhaustive-deps":["warn",{additionalHooks:o.join("|")}]}}]};export{i as config};
|