Loading...
Persist state in localStorage with SSR safety.
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
if (typeof window === 'undefined') return initialValue;
try {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
} catch {
return initialValue;
}
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// Usage
const [theme, setTheme] = useLocalStorage('theme', 'dark');Debounce a value for search inputs and API calls.
import { useState, useEffect } from 'react';
function useDebounce<T>(value: T, delay = 300): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchComponent() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery);
}, [debouncedQuery]);
}Respond to CSS media queries in React.
import { useState, useEffect } from 'react';
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
setMatches(media.matches);
const listener = (e: MediaQueryListEvent) =>
setMatches(e.matches);
media.addEventListener('change', listener);
return () => media.removeEventListener('change', listener);
}, [query]);
return matches;
}
// Usage
const isMobile = useMediaQuery('(max-width: 768px)');
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');Clean patterns for conditional JSX rendering.
// && short-circuit (careful with 0/NaN)
{items.length > 0 && <List items={items} />}
// Ternary
{isLoading ? <Spinner /> : <Content />}
// Early return (recommended for complex cases)
function UserProfile({ user }) {
if (!user) return <NotFound />;
if (user.banned) return <Banned />;
return <Profile user={user} />;
}
// Object map (replaces switch)
const statusComponent = {
loading: <Spinner />,
error: <Error />,
success: <Content />,
}[status];