Common Hooks
A collection of reusable React hooks for common functionality across web and mobile platforms.
Purpose
This library provides a set of utility hooks that work consistently across React (web) and React Native (mobile) platforms, offering common functionality like event handling, window sizing, and lifecycle management.
Features
- Cross-platform compatibility: Works on both React (web) and React Native (mobile)
- Event handling: Automatic cleanup for event listeners and proper event typing
- Lifecycle management: Async effects, one-time effects, and layout effects
- Size tracking: Window and element size tracking with optional debouncing
- State management: Previous value tracking and URL search parameter management
Installation
- npm
- Yarn
npm install @haus-storefront-react/common-hooks
yarn add @haus-storefront-react/common-hooks
Note: This is not a public package. Contact the Haus Tech Team for access.
API Reference
useAsyncEffect
Runs an async effect with proper cleanup handling. Supports both Promise and AsyncGenerator return types.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
effect | () => Promise<void> | AsyncGenerator<void, void, void> | Yes | Async function or async generator to run |
deps | DependencyList | No | Optional dependency array (defaults to undefined) |
Returns
void - No return value
useChangeEvent
Creates a memoized change event handler with proper event typing.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
handler | ChangeEventHandler<T> | Yes | Change event handler function |
T | Element | No | Generic type parameter for the element type (defaults to Element) |
Returns
ChangeEventHandler<T> - Memoized change event handler
useInputChangeEvent
Specialized change handler hook for form elements. It extracts and forwards only event.target.value from input, textarea, and select elements.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
handler | (value: string) => void | Yes | Callback receiving the input value |
Returns
ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> - Memoized input change handler
useEffectOnce
Runs an effect only once on mount. If dependencies are provided, runs when all dependencies are defined and not null.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
effect | EffectCallback | Yes | Effect callback function |
deps | DependencyList | No | Optional dependency array (defaults to empty array) |
Returns
void - No return value
useElementSize
Tracks the size of a DOM element with optional padding exclusion and automatic resize detection.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
includePadding | boolean | No | If true, calculates size excluding padding (subtracts padding from dimensions). Defaults to false which includes padding in the size calculation. |
Returns
[ref: (arg: unknown) => void, size: Size] - Tuple containing ref callback and size object
useEventListener
Adds event listeners with automatic cleanup. Supports Window, Document, HTMLElement, and MediaQueryList targets.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
eventName | K | Yes | Event name from WindowEventMap, DocumentEventMap, HTMLElementEventMap, or MediaQueryListEventMap |
handler | (event: EventType) => void | Yes | Event handler function |
element | RefObject<T> | No | Optional ref object for element-specific events (defaults to undefined, uses window) |
options | boolean | AddEventListenerOptions | No | Optional addEventListener options |
Returns
void - No return value
useIsomorphicLayoutEffect
Layout effect that works on both web and mobile platforms. Uses useLayoutEffect on web and useEffect on React Native.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
effect | EffectCallback | Yes | Effect callback function |
deps | DependencyList | No | Optional dependency array |
Returns
void - No return value
useKeyDown
Handles keyboard events for specific keys with optional preventDefault and stopPropagation options.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | string[] | Yes | Key name(s) to listen for |
handler | KeyDownHandler | Yes | Keyboard event handler function |
options | KeyDownOptions | No | Optional configuration object |
Options Object
interface KeyDownOptions {
preventDefault?: boolean
stopPropagation?: boolean
}
Returns
KeyDownHandler - Memoized keyboard event handler
usePreviousPersistent
Tracks the previous value of a variable using deep equality comparison.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
value | T | Yes | Current value to track |
Returns
T \| null - Previous value or null if no previous value exists
useSearchParams
Manages URL search parameters with automatic synchronization to browser history.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
onParamsChange | ParamsChangeCallback | No | Optional callback function called when search params change |
Returns
[URLSearchParams, UpdateSearchParams] - Tuple containing current search params and update function
useWindowSize
Tracks window size with optional debouncing for performance optimization.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
debounceValue | number | No | Optional debounce delay in milliseconds (web only, ignored in React Native) |
Returns
WindowSize - Object containing width and height
Note: In React Native, this hook uses
useWindowDimensionsand does not support debouncing. ThedebounceValueparameter is ignored in React Native.
Basic Usage
Simple Event Listener
- React
- React Native
import { useEventListener } from '@haus-storefront-react/common-hooks'
function MyComponent() {
useEventListener('scroll', () => {
console.log('Page scrolled')
})
return <div>Scroll to see console output</div>
}
import { Text } from 'react-native'
import { useEventListener } from '@haus-storefront-react/common-hooks'
function MyComponent() {
useEventListener('scroll', () => {
console.log('Page scrolled')
})
return <Text>Scroll to see console output</Text>
}
One-Time Effect
- React
- React Native
import { useEffectOnce } from '@haus-storefront-react/common-hooks'
function MyComponent() {
useEffectOnce(() => {
console.log('Component mounted once')
})
return <div>Check console</div>
}
import { View, Text } from 'react-native'
import { useEffectOnce } from '@haus-storefront-react/common-hooks'
function MyComponent() {
useEffectOnce(() => {
console.log('Component mounted once')
})
return <Text>Check console</Text>
}
Search Params
- React
- React Native
import { useSearchParams } from '@haus-storefront-react/common-hooks'
function MyComponent() {
const [searchParams, setSearchParams] = useSearchParams((newParams) => {
console.log('Params changed:', newParams.toString())
})
const updateParam = (key, value) => {
setSearchParams({ ...Object.fromEntries(searchParams), [key]: value })
}
return (
<div>
<button onClick={() => updateParam('filter', 'active')}>
Filter Active
</button>
</div>
)
}
import { View, Text, Pressable } from 'react-native'
import { useSearchParams } from '@haus-storefront-react/common-hooks'
function MyComponent() {
const [searchParams, setSearchParams] = useSearchParams((newParams) => {
console.log('Params changed:', newParams.toString())
})
const updateParam = (key: string, value: string) => {
setSearchParams({ ...Object.fromEntries(searchParams), [key]: value })
}
return (
<Pressable onPress={() => updateParam('filter', 'active')}>
<Text>Filter Active</Text>
</Pressable>
)
}
Advanced Usage
Async Effect with Cleanup
- React
- React Native
import { useAsyncEffect } from '@haus-storefront-react/common-hooks'
function DataComponent({ userId }) {
useAsyncEffect(async () => {
const controller = new AbortController()
const data = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
}).then((res) => res.json())
// Handle data
console.log('Data loaded:', data)
return () => {
controller.abort()
}
}, [userId])
return <div>Loading user data...</div>
}
import { View, Text } from 'react-native'
import { useAsyncEffect } from '@haus-storefront-react/common-hooks'
function DataComponent({ userId }: { userId: string }) {
useAsyncEffect(async () => {
const controller = new AbortController()
const data = await fetch(`https://example.com/users/${userId}`, {
signal: controller.signal,
}).then((res) => res.json())
// Handle data
console.log('Data loaded:', data)
return () => {
controller.abort()
}
}, [userId])
return (
<View>
<Text>Loading user data...</Text>
</View>
)
}
Keyboard Event Handling
- React
- React Native
import { useKeyDown } from '@haus-storefront-react/common-hooks'
function KeyboardComponent() {
const handleEscape = useKeyDown(
'Escape',
(event) => {
console.log('Escape pressed')
// Close modal, reset form, etc.
},
{
preventDefault: true,
stopPropagation: true,
},
)
const handleMultipleKeys = useKeyDown(['Enter', 'Space'], (event) => {
if (event.key === 'Enter') {
console.log('Enter pressed')
} else {
console.log('Space pressed')
}
})
return (
<div
onKeyDown={(e) => {
handleEscape(e)
handleMultipleKeys(e)
}}
>
Press Escape, Enter, or Space
</div>
)
}
import { useKeyDown } from '@haus-storefront-react/common-hooks'
import { Pressable, Text } from 'react-native'
function KeyboardComponent() {
const handleEscape = useKeyDown('Escape', () => {
console.log('Escape pressed')
// Close modal, reset form, etc.
})
const handleMultipleKeys = useKeyDown(['Enter', 'Space'], (event) => {
if (event.key === 'Enter') {
console.log('Enter pressed')
} else {
console.log('Space pressed')
}
})
return (
<Pressable
accessible
onKeyDown={(event) => {
handleEscape(event)
handleMultipleKeys(event)
}}
>
<Text>Focus here, then press Escape, Enter, or Space</Text>
</Pressable>
)
}
Hooks Composition
- React
- React Native
import {
useEventListener,
usePreviousPersistent,
useWindowSize,
} from '@haus-storefront-react/common-hooks'
function useWindowResize() {
const { width, height } = useWindowSize(100) // 100ms debounce
const previousSize = usePreviousPersistent({ width, height })
useEventListener('resize', () => {
if (
previousSize &&
(width !== previousSize.width || height !== previousSize.height)
) {
console.log('Size changed')
}
})
return {
size: { width, height },
previousSize,
hasChanged:
previousSize &&
(width !== previousSize.width || height !== previousSize.height),
}
}
function MyComponent() {
const { size, hasChanged } = useWindowResize()
return (
<div>
Window: {size.width} x {size.height}
{hasChanged && <div>Size changed!</div>}
</div>
)
}
import {
useEventListener,
usePreviousPersistent,
useWindowSize,
} from '@haus-storefront-react/common-hooks'
import { useMemo } from 'react'
import { View, Text } from 'react-native'
function useWindowResize() {
const { width, height } = useWindowSize()
const previousSize = usePreviousPersistent({ width, height })
useEventListener('resize', () => {
if (
previousSize &&
(width !== previousSize.width || height !== previousSize.height)
) {
console.log('Size changed')
}
})
return useMemo(
() => ({
size: { width, height },
previousSize,
hasChanged:
!!previousSize &&
(width !== previousSize.width || height !== previousSize.height),
}),
[height, previousSize, width],
)
}
function MyComponent() {
const { size, hasChanged } = useWindowResize()
return (
<View>
<Text>
Window: {size.width} x {size.height}
</Text>
{hasChanged && <Text>Size changed!</Text>}
</View>
)
}
Made with ❤️ by Haus Tech Team