@mhmdjawhar/react-hooks
v1.3.1
Published
A collection of hooks for better state management
Downloads
11
Maintainers
Readme
✨ React Hooks
A collection of hooks for better state management.
Advantages of using this library:
- Negligible bundle size (supports tree shaking, ≈400B per hook import 🤏).
- Very detailed documentation along with previews and live demos.
- Highly performant (no unnecessary rerenders at all).
- Super flexible (providing options whenever possible).
- Easily extendable: Since it's tree-shakable, any new useful hooks can be added in the future without having to worry about bundle size.
📕 Table of Content
⬇️ Installation
npm i @mhmdjawhar/react-hooks
🪝 Hooks
- useDisclosure
- useClickOutside
- useViewportSize
- useResizeObserver
- useWindowScroll
- useSystemColorScheme
- useWindowEvent
- useResetChild
- useLocalStorage
- useTimeout
- useInterval
- useDebounce
- useAnimationFrame
- useMediaQuery
🔎 Usage
useDisclosure
Used to manage boolean
state and controlled components.
Examples
import { useDisclosure } from '@mhmdjawhar/react-hooks'
export const Demo = () => {
const [opened, { close, open, toggle }] = useDisclosure()
return (
<>
<button onClick={open}>open</button>  
<button onClick={close}>close</button>  
<button onClick={toggle}>toggle</button>
<p>{opened ? 'opened' : 'closed'}</p>
</>
)
}
Parameters
| Name | Type | Description |
| ------------ | --------- | ------------------------------------------------------- |
| initialValue | boolean
| (Optional) Initial opened
state. Defaults to false
. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ------------ | --------- | -------------------------------------------- |
| [0]
| boolean
| The current state of the disclosure. |
| [1].open
| Function | A function that sets the state to true
. |
| [1].close
| Function | A function that sets the state to false
. |
| [1].toggle
| Function | A function that toggles the boolean
state. |
useClickOutside
Detects click
or an optional given event outside of a given element or list of elements.
Examples
import { useClickOutside } from '@mhmdjawhar/react-hooks'
import { useCallback, useState } from 'react'
export const ClickOutsideExample: React.FC = () => {
const [count, setCount] = useState(0)
const handler = useCallback(() => {
setCount((c) => c + 1)
console.log('outside was clicked')
}, [])
const ref = useClickOutside<HTMLDivElement>(handler)
return (
<>
<div ref={ref} style={{ width: '400px', height: '400px', backgroundColor: 'black' }} />
<p>Click outside count: {count}</p>
</>
)
}
You can also specify multiple nodes. The nodes in the list won't trigger the outside click
event.
import { useClickOutside } from '@mhmdjawhar/react-hooks'
import { useCallback, useRef, useState } from 'react'
export const ClickOutsideMultipleExample: React.FC = () => {
const [count, setCount] = useState(0)
const redBoxRef = useRef<HTMLDivElement>(null)
const blackBoxRef = useRef<HTMLDivElement>(null)
const handler = useCallback(() => {
setCount((c) => c + 1)
console.log('outside was clicked')
}, [])
useClickOutside(handler, [redBoxRef, blackBoxRef])
return (
<>
{/* first element */}
<div ref={redBoxRef} style={{ width: '400px', height: '400px', backgroundColor: 'red' }} />
{/* second element */}
<div ref={blackBoxRef} style={{ width: '400px', height: '400px', backgroundColor: 'black' }} />
<p>Click outside count: {count}</p>
</>
)
}
You can also specify another event to replace the default click
event.
import { useClickOutside } from '@mhmdjawhar/react-hooks'
import { useCallback, useState } from 'react'
export const OutsideEventExample: React.FC = () => {
const [count, setCount] = useState(0)
const handler = useCallback(() => {
setCount((prevCount) => prevCount + 1)
console.log('outside was clicked')
}, [])
const ref = useClickOutside<HTMLDivElement>(handler, null, 'mousedown')
return (
<>
<div ref={ref} style={{ width: '400px', height: '400px', backgroundColor: 'black' }} />
<p>Click outside count: {count}</p>
</>
)
}
Parameters
| Name | Type | Description |
| ------- | ------------------------ | ----------------------------------------------------------------------------------- |
| handler | Function | A callback
function triggered when outside click is detected. |
| refs | RefObject[]
| (Optional) List of RefObject
s for elements that should not trigger outside click. |
| event | keyof DocumentEventMap
| (Optional) Event to replace the default click
event. |
Return Value
| Name | Type | Description |
| ---- | ----------- | ------------------------------------------------------------- |
| ref | RefObject
| Must be passed to the element to detect clicks outside of it. |
useViewportSize
Returns current viewport's width
and height
. It updates on resize
and orientationchange
.
Examples
import { useViewportSize } from '@mhmdjawhar/react-hooks'
export const ViewportSizeExample: React.FC = () => {
const { width, height } = useViewportSize()
return (
<p>
width: {width}, height: {height}
</p>
)
}
You can also pass an optional boolean
parameter to disable or enable debouncing the resize update. It is set true
by default to optimize and avoid too many rerenders. to Set to false
to disable debouncing and get instant updates.
import { useViewportSize } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const ViewportSizeSubscriptionExample: React.FC = () => {
const [debounce, setDebounce] = useState(true)
const { width, height } = useViewportSize(debounce)
return (
<>
<p>
width: {width}, height: {height}
</p>
<button onClick={() => setDebounce((prev) => !prev)}>toggle debounce</button>
</>
)
}
Parameters
| Name | Type | Description |
| -------- | --------- | ---------------------------------------------------------------------------------------------------------------- |
| debounce | boolean
| (Optional) Debounce updating the size. Set to false
to disable debouncing. true
by default for optimization. |
Return Value
Returns an object with the following properties:
| Name | Type | Description |
| ------ | -------- | -------------------- |
| width | number
| The viewport width. |
| height | number
| The viewport height. |
useResizeObserver
Detects changes to the dimensions of an Element
with ResizeObserver
.
Examples
import { useResizeObserver } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const ResizeObserverExample: React.FC = () => {
const [rect, setRect] = useState({
x: 0,
y: 0,
width: 0,
height: 0,
top: 0,
left: 0,
bottom: 0,
right: 0
})
const ref = useResizeObserver<HTMLTextAreaElement>((contentRect) => subscription && setRect(contentRect))
return (
<>
<textarea ref={ref} style={{ width: '400px', height: '400px', position: 'relative' }} />
<p>
width: {rect.width}, height: {rect.height}
</p>
<p>
x: {rect.x}, y: {rect.y}
</p>
<p>
top: {rect.top}, left: {rect.left}, bottom: {rect.bottom}, right: {rect.right}
</p>
</>
)
}
You can also pass a list of dependencies used in the callback
function.
import { useResizeObserver } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const ResizeObserverExample: React.FC = () => {
const [rect, setRect] = useState({
x: 0,
y: 0,
width: 0,
height: 0,
top: 0,
left: 0,
bottom: 0,
right: 0
})
const [subscription, setSubscription] = useState(true)
const ref = useResizeObserver<HTMLTextAreaElement>(
(contentRect) => subscription && setRect(contentRect),
[subscription]
)
return (
<>
<textarea ref={ref} style={{ width: '400px', height: '400px', position: 'relative' }} />
<p>
width: {rect.width}, height: {rect.height}
</p>
<p>
x: {rect.x}, y: {rect.y}
</p>
<p>
top: {rect.top}, left: {rect.left}, bottom: {rect.bottom}, right: {rect.right}
</p>
<button onClick={() => setSubscription((sub) => !sub)}>toggle subscription</button>
</>
)
}
Parameters
| Name | Type | Description |
| -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| handler | Function | A function called when the size of the element observed changes. contentRect
values are passed as an object parameter. |
| depsList | DependencyList
| (Optional) List of dependencies used in the handler
. Pass state values that the handler
might depend on. |
| options | ResizeObserver
| (Optional) ResizeObserver
options. |
Return Value
| Name | Type | Description |
| ---- | ----------- | ----------------------------------------------------------- |
| ref | RefObject
| Must be passed to the element whose size is being observed. |
useWindowScroll
Returns current window
scroll position and a function to scroll to a given position.
Examples
import { useWindowScroll } from '@mhmdjawhar/react-hooks'
export const WindowScrollExample: React.FC = () => {
const [position, scrollTo] = useWindowScroll()
return (
<>
<p>
Scroll position - X: {position.x}, Y: {position.y}
</p>
<button onClick={() => scrollTo({ y: 0 })}>scroll to top</button>
</>
)
}
You can also pass an optional boolean
parameter to either activate or cancel subscription. Set to false
to stop getting updates.
import { useWindowScroll } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const WindowScrollExample: React.FC = () => {
const [subscription, setSubscription] = useState(true)
const [position, scrollTo] = useWindowScroll(subscription)
return (
<>
<p>
Scroll position - width: {position.x}, height: {position.y}
</p>
<button onClick={() => scrollTo({ y: 0 })}>scroll to top</button>
<button onClick={() => setSubscription((prev) => !prev)}>toggle subscription</button>
</>
)
}
Parameters
| Name | Type | Description |
| ------------ | --------- | ----------------------------------------------------------------------------------- |
| isSubscribed | boolean
| (Optional) Activate or cancel subscription. Set to false
to stop getting updates. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ------- | -------- | -------------------------------------------------- |
| [0].x
| number
| Scroll position X. |
| [0].y
| number
| Scroll position Y. |
| [1]
| Function | A function to scroll smoothly to a given position. |
useSystemColorScheme
Returns current system
color scheme. Updates on change.
Examples
import { useSystemColorScheme } from '@mhmdjawhar/react-hooks'
export const SystemColorSchemeExample: React.FC = () => {
const colorScheme = useSystemColorScheme()
return <p>{colorScheme}</p>
}
Return Value
| Name | Type | Description |
| ----------- | -------- | ------------------ |
| colorScheme | string
| light
or dark
. |
useWindowEvent
Adds an event
listener to window
object when the component mounts and removes it when it unmounts.
Examples
import { useWindowEvent } from '@mhmdjawhar/react-hooks'
import { useCallback, useState } from 'react'
export const WindowEventExample: React.FC = () => {
const [key, setKey] = useState('nothing')
const windowListener = useCallback((event: KeyboardEvent) => {
setKey(event.key)
}, [])
useWindowEvent('keydown', windowListener)
return <p>{key} was pressed</p>
}
Parameters
| Name | Type | Description |
| -------- | ------------------------------------ | --------------------------- |
| type | keyof WindowEventMap
| Type of event
. |
| listener | Function | event
listener. |
| options | boolean \| AddEventListenerOptions
| (Optional) event
options. |
useResetChild
Resets the state of a child component along with all its children. Note that it does not reset the state of the current component.
Examples
import { useResetChild } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const ResetChildExample = () => {
const [resetKey, reset] = useResetChild()
return (
<>
<ChildComponent key={resetKey} />
<button onClick={reset}>reset child component</button>
</>
)
}
const ChildComponent = () => {
const [count, setCount] = useState(0)
return (
<div>
<p>Child Component</p>
<button onClick={() => setCount((c) => c + 1)}>count: {count}</button>
<SubChildComponent />
</div>
)
}
const SubChildComponent = () => {
const [count, setCount] = useState(0)
return (
<div>
<p>Subchild Component</p>
<button onClick={() => setCount((c) => c + 1)}>count: {count}</button>
</div>
)
}
Parameters
| Name | Type | Description |
| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| prefixKey | string
| (Optional) In case multiple instances of this hook are being used to reset sibling components. Pass a unique prefix value to avoid key collisions. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ----- | -------- | ------------------------------------------------------- |
| [0]
| string
| Reset value that must be passed to the component key
. |
| [1]
| Function | A reset function to trigger the reset. |
useTimeout
Manages timeout
and handles starting and clearing it.
Examples
import { useTimeout } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const TimeoutExample: React.FC = () => {
const [count, setCount] = useState(0)
const [start, clear] = useTimeout(() => setCount((c) => c + 1), 1000)
return (
<>
<button onClick={start}>Start Timeout</button>
<button onClick={clear}>Clear Timeout</button>
<p>count: {count}</p>
</>
)
}
Parameters
| Name | Type | Description |
| ---------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback | Function | A function that will be called after the timer elapses. |
| delay | number
| timeout
delay (ms
) after which the callback
function will be executed. |
| autoInvoke | boolean
| (Optional) Determines whether the timeout
should start when the component mounts. false
by default. |
| depsList | React.DependencyList
| (Optional) List of dependencies used in the callback
function. Pass state values that the callback
function might depend on. Empty by default. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ----- | -------- | ------------------------- |
| [0]
| Function | Start timeout
function. |
| [1]
| Function | Clear timeout
function. |
useInterval
Manages interval
and handles starting and clearing it.
Examples
import { useInterval } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const IntervalExample: React.FC = () => {
const [count, setCount] = useState(0)
const [start, clear, isActive] = useInterval(() => setCount((c) => c + 1), 1000)
const [status, setStatus] = useState('')
const checkIntervalStatus = () => {
if (isActive()) {
setStatus('running')
} else {
setStatus('idle')
}
}
return (
<>
<button onClick={start}>Start</button>
<button onClick={clear}>Clear</button>
<p>count: {count}</p>
<button onClick={checkIntervalStatus}>check interval status</button>
<p>Interval state: {status}</p>
</>
)
}
Parameters
| Name | Type | Description |
| ---------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback | Function | A function that will be called every delay. |
| delay | number
| interval
delay (ms
) in between callback
executions. |
| autoInvoke | boolean
| (Optional) Determines whether the interval
should start when the component mounts. false
by default. |
| depsList | React.DependencyList
| (Optional) List of dependencies used in the callback
function. Pass state values that the callback
function might depend on. Empty by default. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ----- | -------- | ------------------------------------------------- |
| [0]
| Function | Start interval
function. |
| [1]
| Function | Clear interval
function. |
| [2]
| Function | A function to check the status of the interval
. |
useAnimationFrame
Manages requestAnimationFrame
and handles starting and cancelling it.
Examples
import { useAnimationFrame } from '@mhmdjawhar/react-hooks'
import { useRef, useState } from 'react'
export const RequestAnimationFrameExample: React.FC = () => {
const boxRef = useRef<HTMLDivElement>(null)
// Animation that moves a div 400px over 2 seconds
const [start, , isActive] = useAnimationFrame(({ timestamp, startTime, complete }) => {
const runTime = timestamp - startTime
const duration = 2000
const distance = 400
let progress = runTime / duration
progress = Math.min(progress, 1)
if (boxRef.current) {
boxRef.current.style.left = (distance * progress).toFixed(2) + 'px'
}
// if duration is met stop animation by calling complete()
if (runTime >= duration) {
complete(() => checkAnimationStatus())
}
})
const [status, setStatus] = useState('idle')
const checkAnimationStatus = () => {
if (isActive()) {
setStatus('running')
} else {
setStatus('idle')
}
}
const startAnimation = () => {
start()
checkAnimationStatus()
}
return (
<>
<button onClick={startAnimation}>Start</button>
<div ref={boxRef} style={{ width: '100px', height: '100px', background: 'purple', position: 'relative' }} />
<button onClick={checkAnimationStatus}>check animation status</button>
<p>Animation state: {status}</p>
</>
)
}
Just like useTimeout
and useInterval
, useAnimationFrame
also returns a cancel function that you can use to stop the animation from running.
import { useAnimationFrame } from '@mhmdjawhar/react-hooks'
import { useRef } from 'react'
export const RequestAnimationFrameCancelExample: React.FC = () => {
const boxRef = useRef<HTMLDivElement>(null)
const rightRef = useRef<number>(0)
const leftRef = useRef<number>(400)
// Animation that moves the div 400px to the right over 2 seconds
const [startMoveRight, cancelMoveRight] = useAnimationFrame(({ complete }) => {
if (boxRef.current) {
// run animation as long as distance is not met, otherwise call complete()
if (rightRef.current < 400) {
++rightRef.current
boxRef.current.style.left = rightRef.current + 'px'
} else {
complete(() => {
rightRef.current = 0
startMoveLeft()
})
}
}
})
// Animation that moves the div 400px to the left over 2 seconds
const [startMoveLeft, cancelMoveLeft] = useAnimationFrame(({ complete }) => {
if (boxRef.current) {
// run animation as long as distance is not met, otherwise call complete()
if (leftRef.current > 0) {
--leftRef.current
boxRef.current.style.left = leftRef.current + 'px'
} else {
complete(() => {
leftRef.current = 400
startMoveRight()
})
}
}
})
const cancel = () => {
cancelMoveLeft()
cancelMoveRight()
}
const start = () => {
if (rightRef.current >= 0 && leftRef.current === 400) {
startMoveRight()
} else {
startMoveLeft()
}
}
return (
<>
<button onClick={start}>Start</button>
<button onClick={cancel}>cancel</button>
<div ref={boxRef} style={{ width: '100px', height: '100px', background: 'purple', position: 'relative' }} />
</>
)
}
Parameters
| Name | Type | Description |
| ---------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback | Function | Function that will be called when the next frame is available. timestamp
of requestAnimationFrame
and complete
function are passed as parameters. |
| autoInvoke | boolean
| (Optional) Determines whether the requestAnimationFrame
should start when the component mounts. false
by default. |
| depsList | React.DependencyList
| (Optional) List of dependencies used in the callback
function. Pass state values that the callback
function might depend on. Empty by default. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ----- | -------- | ------------------------------------------------ |
| [0]
| Function | Start animation. |
| [1]
| Function | Cancel animation. |
| [2]
| Function | A function to check the status of the animation. |
useDebounce
Debounces a callback
delaying its execution time since the last call.
Examples
import { useDebounce } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
export const DebounceExample: React.FC = () => {
const [text, setText] = useState('')
const handleTextChange = useDebounce(
(value: string) => {
setText(value)
},
500,
[text]
)
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value
handleTextChange(value)
}
return (
<>
<input onChange={onChange} />
<p>debounced text: {text}</p>
</>
)
}
Parameters
| Name | Type | Description |
| -------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback | Function | Function to be debounced. |
| delay | number | Debounce delay (ms
) after which the callback
function will be executed. |
| depsList | DependencyList
| (Optional) List of dependencies used in the callback
function. Pass state values that the callback
function might depend on. Empty by default. |
Return Value
| Name | Type | Description | | -------- | -------- | --------------------------------------- | | debounce | Function | A function that debounces the callback. |
useLocalStorage
Used to manage local storage items.
Examples
import { useLocalStorage } from '@mhmdjawhar/react-hooks'
import { useState } from 'react'
const initialValue = { firstTime: true, progress: 0 }
export const LocalStorageExample: React.FC = () => {
const [getTutorial, setTutorial, resetTutorial] = useLocalStorage('tutorial', initialValue)
const [state, setState] = useState(getTutorial() || initialValue)
const handleProgressClick = () => {
const tutorial = getTutorial()
setTutorial({ firstTime: false, progress: tutorial ? tutorial.progress + 1 : 1 })
setState(getTutorial())
}
const handleResetClick = () => {
resetTutorial()
setState(getTutorial())
}
return (
<>
<button onClick={handleProgressClick}>progress: {state?.progress || 0}</button>
<button onClick={handleResetClick}>reset tutorial</button>
<p>local Storage value: {JSON.stringify(state)}</p>
</>
)
}
Parameters
| Name | Type | Description |
| ------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| key | string
| Key of the local storage item. |
| initialValue | any | (Optional) Initial value of the item if it doesn't exist yet. If raw
is set to true
make sure initial value is a string
. |
| raw | boolean
| (Optional) If set to true
the stored value will not be JSON serialized. Defaults to false
. |
Return Value
Returns an array with the following elements:
| Name | Type | Description |
| ----- | -------- | ----------------------------------------- |
| [0]
| Function | A function that returns the stored value. |
| [1]
| Function | A function to update the item. |
| [2]
| Function | A function to remove the item. |
useMediaQuery
Listens and checks media query matches.
Examples
import { useMediaQuery } from '@mhmdjawhar/react-hooks'
export const MediaQueryExample: React.FC = () => {
const matches = useMediaQuery('(max-width: 30em)')
return <p>breakpoint matches? {matches ? 'true' : 'false'}</p>
}
Parameters
| Name | Type | Description |
| ------------ | --------- | ----------------------------------------------------------------------- |
| query | string
| Media query string. |
| initialValue | boolean
| (Optional) Initial value of the match query state. Defaults to false
. |
Return Value
| Name | Type | Description |
| ------- | --------- | -------------------------------------------------------------- |
| matches | boolean
| true
if the media query is matched, and false
if it's not. |
💎 Contributions
Any Contributions are welcome!!😄
Feel free to suggest hooks that you think are useful and worth adding to this collection, and work on them if you're interested!
⚖️ License
This library is licensed under the MIT license.