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.
React Native
Some hooks (useElementSize, useInputChangeEvent, useWindowSize, useKeyDown, useSearchParams) have React Native implementations that are picked automatically at runtime via the react-native export condition.
For TypeScript to show the native signatures (instead of the web defaults), import the native type augmentation once at your app's entry point:
import '@haus-storefront-react/shared-types/native'
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
- React (web):
ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>- Memoized input change handler for use withonChange - React Native:
(value: string) => void- Memoized value change handler for use withonChangeTextonTextInput
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 an element with automatic resize detection.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
includePadding | boolean | No | Web only. If true, calculates size excluding padding (subtracts padding from dimensions). Defaults to false which includes padding in the size calculation. |
Returns
- React (web):
[ref: (arg: unknown) => void, size: Size]- Tuple containing a ref callback (use as<div ref={callback} />) and size object - React Native:
[onLayout: (event: LayoutChangeEvent) => void, size: Size]- Tuple containing an onLayout handler (use as<View onLayout={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