Skip to main content

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 install @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

ParameterTypeRequiredDescription
effect() => Promise<void> | AsyncGenerator<void, void, void>YesAsync function or async generator to run
depsDependencyListNoOptional dependency array (defaults to undefined)

Returns

void - No return value

useChangeEvent​

Creates a memoized change event handler with proper event typing.

Parameters

ParameterTypeRequiredDescription
handlerChangeEventHandler<T>YesChange event handler function
TElementNoGeneric 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

ParameterTypeRequiredDescription
handler(value: string) => voidYesCallback receiving the input value

Returns

  • React (web): ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> - Memoized input change handler for use with onChange
  • React Native: (value: string) => void - Memoized value change handler for use with onChangeText on TextInput

useEffectOnce​

Runs an effect only once on mount. If dependencies are provided, runs when all dependencies are defined and not null.

Parameters

ParameterTypeRequiredDescription
effectEffectCallbackYesEffect callback function
depsDependencyListNoOptional dependency array (defaults to empty array)

Returns

void - No return value

useElementSize​

Tracks the size of an element with automatic resize detection.

Parameters

ParameterTypeRequiredDescription
includePaddingbooleanNoWeb 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

ParameterTypeRequiredDescription
eventNameKYesEvent name from WindowEventMap, DocumentEventMap, HTMLElementEventMap, or MediaQueryListEventMap
handler(event: EventType) => voidYesEvent handler function
elementRefObject<T>NoOptional ref object for element-specific events (defaults to undefined, uses window)
optionsboolean | AddEventListenerOptionsNoOptional 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

ParameterTypeRequiredDescription
effectEffectCallbackYesEffect callback function
depsDependencyListNoOptional dependency array

Returns

void - No return value

useKeyDown​

Handles keyboard events for specific keys with optional preventDefault and stopPropagation options.

Parameters

ParameterTypeRequiredDescription
keystring | string[]YesKey name(s) to listen for
handlerKeyDownHandlerYesKeyboard event handler function
optionsKeyDownOptionsNoOptional 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

ParameterTypeRequiredDescription
valueTYesCurrent 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

ParameterTypeRequiredDescription
onParamsChangeParamsChangeCallbackNoOptional 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

ParameterTypeRequiredDescription
debounceValuenumberNoOptional debounce delay in milliseconds (web only, ignored in React Native)

Returns

WindowSize - Object containing width and height

Note: In React Native, this hook uses useWindowDimensions and does not support debouncing. The debounceValue parameter is ignored in React Native.

Basic Usage​

Simple Event Listener​

import { useEventListener } from '@haus-storefront-react/common-hooks'

function MyComponent() {
useEventListener('scroll', () => {
console.log('Page scrolled')
})

return <div>Scroll to see console output</div>
}

One-Time Effect​

import { useEffectOnce } from '@haus-storefront-react/common-hooks'

function MyComponent() {
useEffectOnce(() => {
console.log('Component mounted once')
})

return <div>Check console</div>
}

Search Params​

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>
)
}

Advanced Usage​

Async Effect with Cleanup​

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>
}

Keyboard Event Handling​

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>
)
}

Hooks Composition​

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>
)
}

Made with ❤️ by Haus Tech Team