import { useState, useEffect } from 'react'
export function useDebounce<T>(value: T, delay: number = 500): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
}, [value, delay])
return debouncedValue
}
import { useState, useEffect } from 'react'
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.error('Error reading from localStorage:', error)
return initialValue
}
})
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
console.error('Error writing to localStorage:', error)
}
}
return [storedValue, setValue] as const
}
function SearchPosts() {
const [searchTerm, setSearchTerm] = useState('')
const debouncedSearchTerm = useDebounce(searchTerm, 300)
const { data: posts } = useQuery({
queryKey: ['posts', 'search', debouncedSearchTerm],
queryFn: () => fetchPosts({ search: debouncedSearchTerm }),
enabled: debouncedSearchTerm.length > 2,
})
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search posts..."
/>
{/* Results render here */}
</div>
)
}
Custom hooks extract component logic into reusable functions that can share stateful behavior across components. I prefix hook names with use and compose them from built-in hooks like useState, useEffect, and useCallback. Hooks encapsulate concerns like data fetching, form handling, local storage sync, or WebSocket connections. The key is identifying repeated patterns in components and abstracting them. For example, a useDebounce hook delays value updates, useful for search inputs. A useLocalStorage hook syncs state with browser storage. Custom hooks make components cleaner and logic more testable since hooks can be tested independently from UI rendering.