@kitiumai/utils-react
v3.0.0
Published
Comprehensive React utilities and custom hooks for KitiumAI projects
Maintainers
Readme
@kitiumai/utils-react
The Complete React Utilities Library for Modern Web Applications
A comprehensive, production-ready collection of 60+ React hooks and utilities that combines the best patterns from industry-leading libraries (ahooks, react-use, SWR) with KitiumAI's focus on developer experience, type safety, and performance.
🚀 What is @kitiumai/utils-react?
@kitiumai/utils-react is a modern, tree-shakeable React utilities library designed for teams building complex web applications. It provides battle-tested hooks across all major React development domains: state management, async operations, browser APIs, UI interactions, performance optimization, and more.
Unlike fragmented utility libraries, this package offers a unified API surface with consistent patterns, comprehensive TypeScript support, and SSR-safe implementations.
🎯 Why You Need This Package
Developer Productivity
- 60+ hooks covering every aspect of React development
- Consistent API patterns across all domains
- Comprehensive TypeScript support with full type inference
- Rich JSDoc documentation with copy-paste examples
- Tree-shakeable - import only what you use
Production Reliability
- SSR-safe implementations with proper browser guards
- Error boundaries and comprehensive error handling
- Performance optimized with debouncing, throttling, and caching
- Memory leak prevention with proper cleanup
- Cross-browser compatibility with graceful degradation
Modern React Patterns
- Concurrent mode ready with proper async handling
- Suspense compatible hooks for data fetching
- Custom hooks composition for complex logic
- Event-driven architecture support
- Real-time features with WebSocket and polling support
🏆 Competitor Comparison
| Feature | @kitiumai/utils-react | ahooks | react-use | SWR | |---------|----------------------|--------|-----------|-----| | Total Hooks | 60+ | 60+ | 100+ | 15 | | SSR Safety | ✅ Full | ⚠️ Partial | ⚠️ Partial | ✅ Full | | TypeScript | ✅ First-class | ✅ Good | ⚠️ Limited | ✅ Excellent | | Tree Shaking | ✅ ESM/CJS | ✅ ESM | ✅ ESM | ✅ ESM | | Error Handling | ✅ Comprehensive | ✅ Good | ⚠️ Basic | ✅ Excellent | | Browser APIs | ✅ 15+ hooks | ✅ 10+ hooks | ✅ 20+ hooks | ❌ None | | State Management | ✅ 11 hooks | ✅ 8 hooks | ✅ 15 hooks | ❌ None | | Async/Data | ✅ 6 hooks | ✅ 12 hooks | ✅ 10 hooks | ✅ 15 hooks | | UI/Media | ✅ 6 hooks | ⚠️ Limited | ✅ 15 hooks | ❌ None | | Performance | ✅ 4 hooks | ✅ 6 hooks | ✅ 8 hooks | ⚠️ Limited | | Forms | ✅ 2 hooks | ⚠️ Limited | ✅ 5 hooks | ❌ None | | License | MIT | MIT | MIT | MIT | | Bundle Size | 🟢 Small | 🟡 Medium | 🟡 Large | 🟢 Small |
✨ Unique Selling Proposition (USP)
🎯 Unified Ecosystem Integration
- @kitiumai/logger integration for comprehensive hook error tracking
- @kitiumai/types compatibility for type-safe development
- @kitiumai/config alignment for consistent project setup
- @kitiumai/lint rules for code quality enforcement
🔧 Enterprise-Grade Features
- Advanced async patterns with retry, caching, and request deduplication
- Real-time browser APIs (Geolocation, Battery, Network State, Idle Detection)
- Media controls with full audio/video API coverage
- Speech synthesis and vibration APIs for rich user experiences
- Error boundaries with recovery mechanisms
- Performance monitoring with built-in metrics
🏗️ Architectural Excellence
- Domain-driven organization for predictable imports
- Consistent naming conventions across all hooks
- Composable patterns for complex application logic
- Memory management with automatic cleanup
- Type-safe generics throughout the API surface
📦 Installation
npm install @kitiumai/utils-react
# or
pnpm add @kitiumai/utils-react
# or
yarn add @kitiumai/utils-react🗂️ Complete API Reference
State Management Hooks (11)
useBoolean(initial?: boolean)→[value, { setTrue, setFalse, toggle }]useCounter(initial?: number, options?: { min?: number; max?: number; step?: number })→[count, { increment, decrement, reset, set }]useDefault<T>(value: T, defaultValue: T)→ default value when value is null/undefineduseLatest<T>(value: T)→ always returns the latest value (ref-based)useList<T>(initial?: T[])→[list, { push, insertAt, updateAt, removeAt, clear, reset }]useMap<K, V>(initial?: Iterable<[K, V]>)→[map, { set, get, has, delete, clear, reset }]useQueue<T>(initial?: T[])→[queue, { enqueue, dequeue, peek, clear, size }]useSet<T>(initial?: Iterable<T>)→[set, { add, delete, has, clear, reset }]useSetState<T extends object>(initial: T)→[state, setState](object state management)useStack<T>(initial?: T[])→[stack, { push, pop, peek, clear, size }]useStateWithHistory<T>(initial: T, options?: { capacity?: number; allowDuplicates?: boolean })→[state, setState, { history, pointer, undo, redo, clear }]
Lifecycle Hooks (8)
useFirstMountState()→ boolean indicating if component is mounting for the first timeuseIsomorphicLayoutEffect(effect: EffectCallback, deps?: DependencyList)→ isomorphic layout effectuseMount(fn: () => void)→ run effect only on mountuseMountedState()→ boolean indicating if component is currently mounteduseUnmount(fn: () => void)→ run effect only on unmountuseUpdate(fn: () => void)→ run effect only on updates (not mount)useUpdateEffect(effect: EffectCallback, deps?: DependencyList)→ effect only on updatesuseUpdateLayoutEffect(effect: EffectCallback, deps?: DependencyList)→ layout effect only on updates
Browser API Hooks (16)
useBattery()→{ supported, charging, chargingTime, dischargingTime, level }useClipboard()→[clipboard, { copy, paste, cut }]useElementSize<T extends HTMLElement>()→{ ref, width, height }useGeolocation(options?: PositionOptions)→{ position, loading, error, getCurrentPosition, watchId }useIdle(options?: { timeout?: number; initialState?: boolean })→{ idle, lastActive }useIntersectionObserver(options?: IntersectionObserverInit)→{ ref, entry, isIntersecting }useLocalStorage<T>(key: string, initialValue: T)→[value, setValue, remove]useMediaQuery(query: string)→ boolean matching media queryuseNetworkState()→{ online, downlink, effectiveType, rtt, type }useOnline()→ boolean indicating online statususePageLeave()→ callback when user attempts to leave pageusePermission(name: PermissionName)→{ state, request }useSessionStorage<T>(key: string, initialValue: T)→[value, setValue, remove]useVisibility()→ boolean indicating if page is visibleuseWindowScroll()→{ x, y }window scroll positionuseWindowSize()→{ width, height }window dimensions
Event Hooks (5)
useClickOutside<T extends HTMLElement>(handler: () => void)→[ref]useHover<T extends HTMLElement>()→[ref, isHover]useKeyboard(handler: (event: KeyboardEvent) => void, options?: { event?: 'keydown' | 'keyup' | 'keypress' })→ cleanup functionuseKeyPress(key: string, handler?: (event: KeyboardEvent) => void)→ boolean indicating if key is presseduseMouse()→{ x, y, elementX, elementY, element }
Async Hooks (6)
useAsync<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>)→{ execute, value, error, loading }useAsyncFn<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>)→[state, execute]where state is{ value, error, loading }useAsyncRetry<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, options?: { retryCount?: number; retryDelay?: number })→[state, execute, retry]useInterval(callback: () => void, delay?: number | null)→[start, stop, active]useRequest<TParams, TData>(service: (params: TParams) => Promise<TData>, options?: UseRequestOptions)→ comprehensive data fetching hookuseTimeout(callback: () => void, delay?: number | null)→[start, stop, active]
Performance Hooks (4)
useDebounce<T>(value: T, delay?: number)→ debounced valueuseDebounceCallback<T extends (...args: unknown[]) => unknown>(callback: T, delay?: number, options?: { leading?: boolean; trailing?: boolean })→ debounced callbackuseThrottle<T>(value: T, delay?: number)→ throttled valueuseThrottleCallback<T extends (...args: unknown[]) => unknown>(callback: T, delay?: number)→ throttled callback
Form Hooks (2)
useCheckbox(initial?: boolean)→[checked, { toggle, setChecked, bind }]useInput(initial?: string)→[value, { setValue, reset, bind }]
Utility Hooks (6)
useBeforeUnload(message?: string)→ prevent navigation with confirmationuseError()→[error, setError, clearError]for error state managementuseErrorBoundary(fallback: (props: { error: Error | null; reset: () => void }) => ReactNode)→[ErrorBoundary, resetError]useLockBodyScroll(lock?: boolean)→ lock/unlock body scrolluseScript(src: string, options?: { async?: boolean; defer?: boolean })→ script loading stateuseTitle(title: string)→ set document title
UI Hooks (6)
useAudio(src?: string)→ comprehensive audio controls and stateuseFullscreen()→{ ref, fullscreen, enter, exit, toggle }useModal()→[visible, { open, close, toggle }]useSpeech()→{ speak, speaking, supported, pause, resume, stop }useVibrate(pattern?: number | number[])→ vibration controluseVideo(src?: string)→ comprehensive video controls and state
Root-Level Hooks (2)
usePrevious<T>(value: T)→ previous value of a variableuseToggle(initial?: boolean)→[value, toggle]
📚 Usage Examples
Advanced State Management
import { useMap, useQueue, useStateWithHistory } from '@kitiumai/utils-react';
function AdvancedStateDemo() {
// Map state management
const [userMap, { set: setUser, get: getUser, has: hasUser }] = useMap([
['alice', { name: 'Alice', role: 'admin' }],
['bob', { name: 'Bob', role: 'user' }]
]);
// Queue for processing tasks
const [taskQueue, { enqueue, dequeue, peek }] = useQueue<string>();
// State with undo/redo
const [count, setCount, { undo, redo, history }] = useStateWithHistory(0, {
capacity: 10,
allowDuplicates: false
});
return (
<div>
<h3>Users: {userMap.size}</h3>
<button onClick={() => setUser('charlie', { name: 'Charlie', role: 'moderator' })}>
Add User
</button>
<h3>Task Queue</h3>
<button onClick={() => enqueue(`Task ${Date.now()}`)}>Add Task</button>
<button onClick={() => dequeue()}>Process Task</button>
<p>Next: {peek()}</p>
<h3>Counter with History</h3>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={undo} disabled={!history.canUndo}>Undo</button>
<button onClick={redo} disabled={!history.canRedo}>Redo</button>
</div>
);
}Advanced Data Fetching
import { useRequest } from '@kitiumai/utils-react';
interface User {
id: number;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: number }) {
const {
data: user,
loading,
error,
refresh,
cancel
} = useRequest<User>(
(id: number) => fetch(`/api/users/${id}`).then(r => r.json()),
{
defaultParams: userId,
refreshOnWindowFocus: true,
pollingInterval: 30000, // Poll every 30 seconds
retry: { count: 3, delay: 1000 },
cacheTime: 5 * 60 * 1000, // 5 minutes
onSuccess: (data) => console.log('User loaded:', data),
onError: (err) => console.error('Failed to load user:', err)
}
);
if (loading) return <div>Loading user...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
<button onClick={refresh}>Refresh</button>
<button onClick={cancel}>Cancel</button>
</div>
);
}Browser APIs & Real-time Features
import { useGeolocation, useBattery, useNetworkState, useIdle } from '@kitiumai/utils-react';
function DeviceDashboard() {
// Geolocation tracking
const { position, loading: locationLoading, error: locationError, getCurrentPosition } = useGeolocation({
enableHighAccuracy: true,
timeout: 10000
});
// Battery monitoring
const { supported: batterySupported, level, charging } = useBattery();
// Network state
const { online, effectiveType, downlink } = useNetworkState();
// Idle detection
const { idle, lastActive } = useIdle({ timeout: 5 * 60 * 1000 }); // 5 minutes
return (
<div>
<h2>Device Status</h2>
<section>
<h3>Location</h3>
{locationLoading && <p>Getting location...</p>}
{locationError && <p>Location error: {locationError.message}</p>}
{position && (
<p>
📍 {position.latitude.toFixed(4)}, {position.longitude.toFixed(4)}
</p>
)}
<button onClick={getCurrentPosition}>Update Location</button>
</section>
<section>
<h3>Battery</h3>
{batterySupported ? (
<p>
🔋 {Math.round(level * 100)}% {charging ? '(Charging)' : ''}
</p>
) : (
<p>Battery monitoring not supported</p>
)}
</section>
<section>
<h3>Network</h3>
<p>
🌐 {online ? 'Online' : 'Offline'} - {effectiveType} ({downlink} Mbps)
</p>
</section>
<section>
<h3>Activity</h3>
<p>
{idle ? '😴 Idle' : '👀 Active'} - Last active: {new Date(lastActive).toLocaleTimeString()}
</p>
</section>
</div>
);
}Media Controls & UI
import { useAudio, useVideo, useFullscreen, useSpeech } from '@kitiumai/utils-react';
function MediaPlayer({ audioSrc, videoSrc }: { audioSrc: string; videoSrc: string }) {
// Audio controls
const {
playing: audioPlaying,
volume: audioVolume,
muted: audioMuted,
currentTime: audioTime,
duration: audioDuration,
play: playAudio,
pause: pauseAudio,
setVolume: setAudioVolume,
seek: seekAudio
} = useAudio(audioSrc);
// Video controls
const {
playing: videoPlaying,
fullscreen,
play: playVideo,
pause: pauseVideo,
enterFullscreen,
exitFullscreen
} = useVideo(videoSrc);
// Speech synthesis
const { speak, speaking, supported: speechSupported } = useSpeech();
return (
<div>
<h2>Media Player</h2>
<section>
<h3>Audio Player</h3>
<audio src={audioSrc} controls />
<div>
<button onClick={audioPlaying ? pauseAudio : playAudio}>
{audioPlaying ? '⏸️' : '▶️'}
</button>
<input
type="range"
min="0"
max="1"
step="0.1"
value={audioVolume}
onChange={(e) => setAudioVolume(parseFloat(e.target.value))}
/>
<span>{Math.round(audioTime)}s / {Math.round(audioDuration)}s</span>
</div>
</section>
<section>
<h3>Video Player</h3>
<video src={videoSrc} style={{ width: '400px' }} />
<div>
<button onClick={videoPlaying ? pauseVideo : playVideo}>
{videoPlaying ? '⏸️' : '▶️'}
</button>
<button onClick={fullscreen ? exitFullscreen : enterFullscreen}>
{fullscreen ? '🗗️' : '🗖️'}
</button>
</div>
</section>
{speechSupported && (
<section>
<h3>Text-to-Speech</h3>
<button
onClick={() => speak('Hello! This is a text-to-speech demo.')}
disabled={speaking}
>
{speaking ? 'Speaking...' : 'Speak Demo'}
</button>
</section>
)}
</div>
);
}Error Handling & Recovery
import { useErrorBoundary, useError } from '@kitiumai/utils-react';
function AppWithErrorBoundary() {
const [ErrorBoundary, resetError] = useErrorBoundary(({ error, reset }) => (
<div className="error-fallback">
<h2>🚨 Something went wrong!</h2>
<p>{error?.message}</p>
<button onClick={reset}>Try Again</button>
<button onClick={resetError}>Reset All Errors</button>
</div>
));
return (
<ErrorBoundary>
<MainApp />
</ErrorBoundary>
);
}
function ComponentWithErrorHandling() {
const [error, setError, clearError] = useError();
const handleRiskyOperation = async () => {
try {
await riskyApiCall();
} catch (err) {
setError(err instanceof Error ? err : new Error(String(err)));
}
};
return (
<div>
<button onClick={handleRiskyOperation}>Do Risky Thing</button>
{error && (
<div className="error-message">
<p>Error: {error.message}</p>
<button onClick={clearError}>Dismiss</button>
</div>
)}
</div>
);
}🏗️ Architecture & Best Practices
Import Patterns
// Recommended: Import from specific domains for better tree-shaking
import { useRequest } from '@kitiumai/utils-react/hooks/async';
import { useLocalStorage } from '@kitiumai/utils-react/hooks/browser';
import { useMap, useQueue } from '@kitiumai/utils-react/hooks/state';
// Also supported: Import from root (larger bundle)
import { useRequest, useLocalStorage, useMap } from '@kitiumai/utils-react';Composition Patterns
// Custom hook composition
function useUserProfile(userId: number) {
const { data: user, loading, error, refresh } = useRequest(
(id) => api.getUser(id),
{ defaultParams: userId, refreshOnWindowFocus: true }
);
const [preferences, setPreferences] = useLocalStorage(
`user-prefs-${userId}`,
{ theme: 'light', notifications: true }
);
return { user, preferences, setPreferences, loading, error, refresh };
}
// Usage
function UserProfile({ userId }: { userId: number }) {
const { user, preferences, setPreferences, loading } = useUserProfile(userId);
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>{user?.name}</h1>
<ThemeSelector
theme={preferences.theme}
onChange={(theme) => setPreferences(prev => ({ ...prev, theme }))}
/>
</div>
);
}Performance Optimization
// Debounced search with request deduplication
function SearchComponent() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
const { data: results, loading } = useRequest(
(q: string) => searchAPI(q),
{
defaultParams: debouncedQuery,
ready: debouncedQuery.length > 2, // Don't search for short queries
cacheTime: 5 * 60 * 1000, // Cache results for 5 minutes
retry: { count: 2, delay: 500 }
}
);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
{loading && <div>Searching...</div>}
{results && <SearchResults results={results} />}
</div>
);
}🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
MIT © KitiumAI
