Package Exports
- @alessiofrittoli/react-hooks
- @alessiofrittoli/react-hooks/browser-api
- @alessiofrittoli/react-hooks/browser-api/storage
- @alessiofrittoli/react-hooks/dom-api
- @alessiofrittoli/react-hooks/eslint
- @alessiofrittoli/react-hooks/misc
Readme
React Hooks 🪝
TypeScript React utility Hooks
Table of Contents
Getting started
Run the following command to start using react-hooks in your projects:
npm i @alessiofrittoli/react-hooksor using pnpm
pnpm i @alessiofrittoli/react-hooksESLint Configuration
This library may define and exports hooks that requires additional ESLint configuration for your project such as useUpdateEffect.
Simply imports recommended configuration from @alessiofrittoli/react-hooks/eslint and add them to your ESLint configuration like so:
import { config as AFReactHooksEslint } from '@alessiofrittoli/react-hooks/eslint'
/** @type {import('eslint').Linter.Config[]} */
const config = [
...AFReactHooksEslint.recommended,
// ... other configurations
]
export default configAPI Reference
Browser API
Storage
The following storage hooks use Storage Utilities from @alessiofrittoli/web-utils adding a React oriented implementation.
useStorage
Easly handle Local or Session Storage State.
Type parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
T |
any |
string |
A custom type applied to the stored item. |
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
string |
- | The storage item key. |
initialValue |
T |
- | The storage item initial value. |
type |
local|session |
local | (Optional) The storage API to use. |
Returns
Type: [ Value<T>, SetValue<Value<T>> ]
A tuple with the stored item value or initial value and the setter function.
Usage
Importing the hooks
import {
useStorage, useLocalStorage, useSessionStorage
} from '@alessiofrittoli/react-hooks'
// or
import {
useStorage, useLocalStorage, useSessionStorage
} from '@alessiofrittoli/react-hooks/browser-api'
// or
import {
useStorage, useLocalStorage, useSessionStorage
} from '@alessiofrittoli/react-hooks/browser-api/storage'Reading item value from storage
'use client'
import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
type Locale = 'it' | 'en'
const storage = 'local' // or 'session'
const defaultLocale = 'it'
export const SomeComponent: React.FC = () => {
const [ userLocale ] = useStorage<Locale>( 'user-locale', defaultLocale, storage )
return (
...
)
}Updating storage item value
'use client'
import { useCallback } from 'react'
import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
type Locale = 'it' | 'en'
const storage = 'local' // or 'session'
const defaultLocale = 'it'
export const LanguageSwitcher: React.FC = () => {
const [ userLocale, setUserLocale ] = useStorage<Locale>( 'user-locale', defaultLocale, storage )
const clickHandler = useCallback( () => {
setUserLocale( 'en' )
}, [ setUserLocale ] )
return (
...
)
}Deleting storage item
'use client'
import { useCallback } from 'react'
import { useStorage } from '@alessiofrittoli/react-hooks/browser-api/storage'
type Locale = 'it' | 'en'
const storage = 'local' // or 'session'
const defaultLocale = 'it'
export const LanguageSwitcher: React.FC = () => {
const [ userLocale, setUserLocale ] = useStorage<Locale>( 'user-locale', defaultLocale, storage )
const deleteHandler = useCallback( () => {
setUserLocale( null )
// or
setUserLocale( undefined )
// or
setUserLocale( '' )
}, [ setUserLocale ] )
return (
...
)
}useLocalStorage
Shortcut React Hook for useStorage.
Applies the same API Reference.
useSessionStorage
Shortcut React Hook for useStorage.
Applies the same API Reference.
useMediaQuery
Get Document Media matches and listen for changes.
Parameters
| Parameter | Type | Description |
|---|---|---|
query |
string |
A string specifying the media query to parse into a MediaQueryList. |
Returns
Type: boolean
trueif the document currently matches the media query list.falseotherwise.
Usage
Importing the hook
import { useMediaQuery } from '@alessiofrittoli/react-hooks'
// or
import { useMediaQuery } from '@alessiofrittoli/react-hooks/browser-api'Check if user device prefers dark color scheme
const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )useIsPortrait
Check if device is portrait oriented.
React State get updated when device orientation changes.
Returns
Type: boolean
trueif the device is portrait oriented.falseotherwise.
Usage
Importing the hook
import { useIsPortrait } from '@alessiofrittoli/react-hooks'
// or
import { useIsPortrait } from '@alessiofrittoli/react-hooks/browser-api'Check if user device is in landscape
const isLandscape = ! useIsPortrait()DOM API
useScrollBlock
Prevent Element overflow.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
target |
React.RefObject<HTMLElement|null> |
Document.documentElement |
(Optional) The React RefObject target HTMLElement. |
Returns
Type: [ () => void, () => void ]
A tuple with block and restore scroll callbacks.
Usage
Importing the hook
import { useScrollBlock } from '@alessiofrittoli/react-hooks'
// or
import { useScrollBlock } from '@alessiofrittoli/react-hooks/dom-api'Block Document Overflow
const [ blockScroll, restoreScroll ] = useScrollBlock()
const openPopUpHandler = useCallback( () => {
...
blockScroll()
}, [ blockScroll ] )
const closePopUpHandler = useCallback( () => {
...
restoreScroll()
}, [ restoreScroll ] )
...Block HTML Element Overflow
const elementRef = useRef<HTMLDivElement>( null )
const [ blockScroll, restoreScroll ] = useScrollBlock( elementRef )
const scrollBlockHandler = useCallback( () => {
...
blockScroll()
}, [ blockScroll ] )
const scrollRestoreHandler = useCallback( () => {
...
restoreScroll()
}, [ restoreScroll ] )
...useFocusTrap
Trap focus inside the given HTML Element.
This comes pretty handy when rendering a modal that shouldn't be closed without a user required action.
Parameters
| Parameter | Type | Description |
|---|---|---|
target |
React.RefObject<HTMLElement|null> |
The target HTMLElement React RefObject to trap focus within. |
If no target is given, you must provide the target HTMLElement when calling setFocusTrap. |
Returns
Type: readonly [ SetFocusTrap, RestoreFocusTrap ]
A tuple containing:
setFocusTrap: A function to enable the focus trap. Optionally accept an HTMLElement as target.restoreFocusTrap: A function to restore the previous focus state.
Usage
Importing the hook
import { useFocusTrap } from '@alessiofrittoli/react-hooks'
// or
import { useFocusTrap } from '@alessiofrittoli/react-hooks/dom-api'Defining the target on hook initialization
const modalRef = useRef<HTMLDivElement>( null )
const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap( modalRef )
const modalOpenHandler = useCallback( () => {
if ( ! modalRef.current ) return
// ... open modal
setFocusTrap()
modalRef.current.focus() // focus the dialog so next tab will focus the next element inside the modal
}, [ setFocusTrap ] )
const modalCloseHandler = useCallback( () => {
// ... close modal
restoreFocusTrap() // cancel focus trap and restore focus to the last active element before enablig the focus trap
}, [ restoreFocusTrap ] )Defining the target ondemand
const modalRef = useRef<HTMLDivElement>( null )
const modal2Ref = useRef<HTMLDivElement>( null )
const [ setFocusTrap, restoreFocusTrap ] = useFocusTrap()
const modalOpenHandler = useCallback( () => {
if ( ! modalRef.current ) return
// ... open modal
setFocusTrap( modalRef.current )
modalRef.current.focus()
}, [ setFocusTrap ] )
const modal2OpenHandler = useCallback( () => {
if ( ! modal2Ref.current ) return
// ... open modal
setFocusTrap( modal2Ref.current )
modal2Ref.current.focus()
}, [ setFocusTrap ] )Miscellaneous
useIsClient
Check if the React Hook or Component where this hook is executed is running in a browser environment.
This is pretty usefull to avoid hydration errors.
Returns
Type: boolean
trueif the React Hook or Component is running in a browser environment.falseotherwise.
Usage
Importing the hook
import { useIsClient } from '@alessiofrittoli/react-hooks'
// or
import { useIsClient } from '@alessiofrittoli/react-hooks/misc'Basic usage
'use client'
import { useIsClient } from '@alessiofrittoli/react-hooks/misc'
export const ClientComponent: React.FC = () => {
const isClient = useIsClient()
return (
<div>Running { ! isClient ? 'server' : 'client' }-side</div>
)
}useIsFirstRender
Check if is first React Hook/Component render.
Returns
Type: boolean
trueat the mount time.falseotherwise.
Note that if the React Hook/Component has no state updates, useIsFirstRender will always return true.
Usage
Importing the hook
import { useIsFirstRender } from '@alessiofrittoli/react-hooks'
// or
import { useIsFirstRender } from '@alessiofrittoli/react-hooks/misc'Basic usage
'use client'
import { useIsFirstRender } from '@alessiofrittoli/react-hooks/misc'
export const ClientComponent: React.FC = () => {
const isFirstRender = useIsFirstRender()
const [ counter, setCounter ] = useState( 0 )
useEffect( () => {
const intv = setInterval( () => {
setCounter( prev => prev + 1 )
}, 1000 )
return () => clearInterval( intv )
}, [] )
return (
<div>
{ isFirstRender ? 'First render' : 'Subsequent render' }
<hr />
{ counter }
</div>
)
}useUpdateEffect
Modified version of useEffect that skips the first render.
Parameters
| Parameter | Type | Description |
|---|---|---|
effect |
React.EffectCallback |
Imperative function that can return a cleanup function. |
deps |
React.DependencyList |
If present, effect will only activate if the values in the list change. |
Usage
Importing the hook
import { useUpdateEffect } from '@alessiofrittoli/react-hooks'
// or
import { useUpdateEffect } from '@alessiofrittoli/react-hooks/misc'Basic usage
'use client'
import { useEffect, useState } from 'react'
import { useUpdateEffect } from '@alessiofrittoli/react-hooks/misc'
export const ClientComponent: React.FC = () => {
const [ count, setCount ] = useState( 0 )
useEffect( () => {
const intv = setInterval( () => {
setCount( prev => prev + 1 )
}, 1000 )
return () => clearInterval( intv )
}, [] )
useEffect( () => {
console.log( 'useEffect', count ) // starts from 0
return () => {
console.log( 'useEffect - clean up', count ) // starts from 0
}
}, [ count ] )
useUpdateEffect( () => {
console.log( 'useUpdateEffect', count ) // starts from 1
return () => {
console.log( 'useUpdateEffect - clean up', count ) // starts from 1
}
}, [ count ] )
return (
<div>{ count }</div>
)
}usePagination
Get pagination informations based on the given options.
This hook memoize the returned result of the paginate function imported from @alessiofrittoli/math-utils.
See paginate function Documentation for more information about it.
Development
Install depenendencies
npm installor using pnpm
pnpm iBuild the source code
Run the following command to test and build code for distribution.
pnpm buildESLint
warnings / errors check.
pnpm lintJest
Run all the defined test suites by running the following:
# Run tests and watch file changes.
pnpm test:watch
# Run tests in a CI environment.
pnpm test:ci- See
package.jsonfile scripts for more info.
Run tests with coverage.
An HTTP server is then started to serve coverage files from ./coverage folder.
⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.
test:coverage:serveContributing
Contributions are truly welcome!
Please refer to the Contributing Doc for more information on how to start contributing to this project.
Help keep this project up to date with GitHub Sponsor.
Security
If you believe you have found a security vulnerability, we encourage you to responsibly disclose this and NOT open a public issue. We will investigate all legitimate reports. Email security@alessiofrittoli.it to disclose any security vulnerabilities.
Made with ☕
|
|
|