A collection of zero-dependency useful hooks and components for React.
yarn add react-essentials-functions
# or
npm install react-essentials-functionsHook that detects clicks outside of a referenced element. Useful for closing dropdowns, modals, and popovers. Listens for both mousedown and touchstart events.
Parameters:
ref: RefObject<HTMLElement>- React ref to the element to monitorhandler: (event: MouseEvent | TouchEvent) => void- Callback fired when a click outside is detected
Returns:
void
Side effects:
- Adds
mousedownandtouchstartevent listeners ondocument - Removes listeners on unmount
Example:
import { useClickOutside } from 'react-essentials-functions';
import { useRef, useState } from 'react';
function Dropdown() {
const dropdownRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);
useClickOutside(dropdownRef, () => setIsOpen(false));
return (
<div ref={dropdownRef}>
<button onClick={() => setIsOpen(true)}>Open</button>
{isOpen && <ul><li>Option 1</li><li>Option 2</li></ul>}
</div>
);
}Hook that debounces a value by a given delay. The debounced value will only update after the specified delay has passed since the last change. Useful for search inputs, API calls, and any rapid-fire updates.
Type Parameters:
T- The type of the value to debounce
Parameters:
value: T- The value to debouncedelay: number- The debounce delay in milliseconds
Returns:
T- The debounced value
Example:
import { useDebounce } from 'react-essentials-functions';
import { useState, useEffect } from 'react';
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 300);
useEffect(() => {
if (debouncedSearch) {
// This only fires 300ms after the user stops typing
fetchResults(debouncedSearch);
}
}, [debouncedSearch]);
return (
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}Hook to get the dimensions of a DOM element. Uses ResizeObserver for optimal performance with a fallback to window events for older browsers.
Parameters:
targetRef: RefObject<HTMLElement>- React ref to the element to measure
Returns:
Dimensions- Object containingwidthandheightof the element
Side effects:
- Creates a
ResizeObserveron the target element (or falls back toresize/scrollwindow listeners) - Cleans up on unmount
Example:
import { useDimensions } from 'react-essentials-functions';
import { useRef } from 'react';
function MyComponent() {
const targetRef = useRef<HTMLDivElement>(null);
const { width, height } = useDimensions(targetRef);
return (
<div ref={targetRef}>
Size: {width}px x {height}px
</div>
);
}Hook that syncs state with localStorage. Handles JSON serialization/deserialization automatically. Falls back gracefully when localStorage is unavailable (SSR, private browsing).
Type Parameters:
T- The type of the stored value
Parameters:
key: string- The localStorage keyinitialValue: T- The initial value if nothing is stored
Returns:
[T, (value: T | ((prev: T) => T)) => void, () => void]- A tuple containing:- The current stored value
- A setter function (accepts value or updater function)
- A remove function to clear the key from localStorage
Side effects:
- Reads from
localStorageon initialization - Writes to
localStorageon every value change removeValuedeletes the key fromlocalStorage
Example:
import { useLocalStorage } from 'react-essentials-functions';
function Settings() {
const [name, setName, removeName] = useLocalStorage('user-name', '');
const [preferences, setPreferences] = useLocalStorage('prefs', {
notifications: true,
language: 'en',
});
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button onClick={removeName}>Clear name</button>
<button onClick={() => setPreferences(prev => ({ ...prev, language: 'fr' }))}>
Switch to French
</button>
</div>
);
}Hook that tracks whether a CSS media query matches. Listens for changes and updates automatically. Useful for responsive design, detecting dark mode preference, reduced motion, etc.
Parameters:
query: string- The CSS media query string (e.g.'(min-width: 768px)')
Returns:
boolean- Whether the media query currently matches
Side effects:
- Adds a
changelistener on theMediaQueryListobject - Removes listener on unmount or query change
Example:
import { useMediaQuery } from 'react-essentials-functions';
function ResponsiveComponent() {
const isMobile = useMediaQuery('(max-width: 767px)');
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
return (
<div>
{isMobile ? <MobileLayout /> : <DesktopLayout />}
{prefersDark && <span>Dark mode detected</span>}
</div>
);
}Hook that returns the previous value of a variable. Useful for comparing current and previous props or state values.
Type Parameters:
T- The type of the tracked value
Parameters:
value: T- The value to track
Returns:
T | undefined- The value from the previous render, orundefinedon first render
Example:
import { usePrevious } from 'react-essentials-functions';
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const previousCount = usePrevious(count);
return (
<div>
<p>Current: {count}, Previous: {previousCount ?? 'N/A'}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Hook that provides a fetch function which automatically aborts previous requests and cleans up on unmount using AbortController. Prevents race conditions when multiple requests are made in sequence.
Parameters:
- None
Returns:
(url: string, options?: RequestInit) => Promise<Response>- Fetch function with automatic abort handling
Side effects:
- Aborts the previous in-flight request when a new one is made
- Aborts any pending request on component unmount
- User-provided
signalin options is ignored (the hook manages its own)
Example:
import { useSafeFetch } from 'react-essentials-functions';
import { useEffect } from 'react';
function DataComponent() {
const safeFetch = useSafeFetch();
useEffect(() => {
const fetchData = async () => {
try {
const response = await safeFetch('https://api.example.com/data');
const data = await response.json();
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
}
};
fetchData();
}, [safeFetch]);
return <div>Loading data...</div>;
}A version of useState that prevents state updates after the component unmounts, preventing memory leaks and "Can't perform a React state update on an unmounted component" warnings.
Type Parameters:
T- The type of the state value
Parameters:
initialValue: T | (() => T)- The initial state value
Returns:
[T, (value: T | ((prevState: T) => T)) => void]- A tuple containing the current state and a safe setState function
Example:
import { useSafeState } from 'react-essentials-functions';
import { useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useSafeState<User | null>(null);
useEffect(() => {
fetchUser(userId).then((data) => {
// Safe even if component unmounted during fetch
setUser(data);
});
}, [userId]);
return user ? <div>{user.name}</div> : <div>Loading...</div>;
}Hook to dynamically load external scripts with status tracking and callback support.
Parameters:
url: string- The URL of the script to loadoptions?: UseScriptOptions- Optional configuration:onLoad?: () => void- Callback when script loads successfullyonError?: () => void- Callback when script fails to loadremoveOnUnmount?: boolean- Whether to remove script on unmount (default:true)
Returns:
UseScriptStatus- The current status:'idle' | 'loading' | 'ready' | 'error'
Side effects:
- Appends a
<script>tag todocument.body - Removes the script tag on unmount (unless
removeOnUnmount: false) - Detects and reuses already-loaded scripts
Example:
import { useScript } from 'react-essentials-functions';
function GoogleMapsComponent() {
const status = useScript('https://maps.googleapis.com/maps/api/js', {
onLoad: () => console.log('Google Maps loaded'),
onError: () => console.error('Failed to load Google Maps'),
});
if (status === 'loading') return <div>Loading map...</div>;
if (status === 'error') return <div>Error loading map</div>;
if (status === 'idle') return <div>Initializing...</div>;
return <div>Map is ready!</div>;
}Hook to manage theme (light/dark) with localStorage persistence and SSR support. Automatically detects system color scheme preference via prefers-color-scheme when no theme has been previously stored.
Returns:
[ThemeMode, () => void, boolean]- A tuple containing:- Current theme mode (
'light' | 'dark') - Function to toggle between themes
- Boolean indicating if component is mounted (useful for SSR)
- Current theme mode (
Side effects:
- Reads/writes to
localStoragewith key'theme' - Detects system
prefers-color-schemepreference on first load
Example:
import { useTheme } from 'react-essentials-functions';
function ThemeToggle() {
const [theme, toggleTheme, mounted] = useTheme();
// Avoid hydration mismatch
if (!mounted) return null;
return (
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</button>
);
}Hook for managing a boolean toggle state. Provides a simple API for toggling, setting true, or setting false. Useful for modals, dropdowns, accordions, etc.
Parameters:
initialValue?: boolean- The initial boolean value (default:false)
Returns:
[boolean, () => void, () => void, () => void]- A tuple containing:- The current boolean value
toggle- Flips the valuesetTrue- Sets totruesetFalse- Sets tofalse
Example:
import { useToggle } from 'react-essentials-functions';
function Modal() {
const [isOpen, toggleOpen, open, close] = useToggle(false);
return (
<div>
<button onClick={open}>Open Modal</button>
{isOpen && (
<div className="modal">
<p>Modal content</p>
<button onClick={close}>Close</button>
</div>
)}
</div>
);
}Hook to get the current window dimensions with automatic updates on resize. SSR-safe (returns 0 for both dimensions when window is unavailable).
Returns:
WindowDimensions- Object containingwidthandheightof the window
Side effects:
- Adds a
resizeevent listener onwindow - Removes listener on unmount
Example:
import { useWindowDimensions } from 'react-essentials-functions';
function ResponsiveComponent() {
const { width, height } = useWindowDimensions();
return (
<div>
Window size: {width}px x {height}px
{width < 768 ? <MobileLayout /> : <DesktopLayout />}
</div>
);
}Component that conditionally wraps its children with a wrapper component based on a condition. When the condition is false, children are rendered unwrapped inside a fragment.
Props:
condition: boolean- Whether to wrap the childrenwrapper: (children: React.ReactNode) => JSX.Element- Function that returns the wrapper elementchildren: React.ReactNode- Children to wrap
Example:
import { ConditionalWrapper } from 'react-essentials-functions';
function LinkWrapper({ link, children }) {
return (
<ConditionalWrapper
condition={!!link}
wrapper={(c) => <a href={link}>{c}</a>}
>
<button>{children}</button>
</ConditionalWrapper>
);
}This library is written in TypeScript and includes full type definitions. All types are exported for your convenience:
import type {
Dimensions,
WindowDimensions,
ThemeMode,
UseScriptStatus,
UseScriptOptions,
ConditionalWrapperProps,
} from 'react-essentials-functions';MIT