react-hooks

Reusable hook patterns for common UI scenarios.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-hooks" with this command: npx skills add alicoder001/agent-skills/alicoder001-agent-skills-react-hooks

React Custom Hooks

Reusable hook patterns for common UI scenarios.

Instructions

  1. useDebounce

function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]);

return debouncedValue; }

// Usage function SearchInput() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 300);

useEffect(() => { if (debouncedQuery) { searchAPI(debouncedQuery); } }, [debouncedQuery]); }

  1. useLocalStorage

function useLocalStorage<T>(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState<T>(() => { if (typeof window === 'undefined') return initialValue; try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { 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); } };

return [storedValue, setValue] as const; }

// Usage const [theme, setTheme] = useLocalStorage('theme', 'dark');

  1. useMediaQuery

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 function Component() { const isMobile = useMediaQuery('(max-width: 768px)'); const isDark = useMediaQuery('(prefers-color-scheme: dark)'); }

  1. useClickOutside

function useClickOutside<T extends HTMLElement>( handler: () => void ): RefObject<T> { const ref = useRef<T>(null);

useEffect(() => { const listener = (event: MouseEvent | TouchEvent) => { if (!ref.current || ref.current.contains(event.target as Node)) { return; } handler(); };

document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
  document.removeEventListener('mousedown', listener);
  document.removeEventListener('touchstart', listener);
};

}, [handler]);

return ref; }

// Usage function Dropdown() { const [isOpen, setIsOpen] = useState(false); const ref = useClickOutside<HTMLDivElement>(() => setIsOpen(false));

return <div ref={ref}>{isOpen && <Menu />}</div>; }

  1. useToggle

function useToggle(initialValue = false) { const [value, setValue] = useState(initialValue);

const toggle = useCallback(() => setValue(v => !v), []); const setTrue = useCallback(() => setValue(true), []); const setFalse = useCallback(() => setValue(false), []);

return { value, toggle, setTrue, setFalse }; }

// Usage const { value: isOpen, toggle, setFalse: close } = useToggle();

  1. usePrevious

function usePrevious<T>(value: T): T | undefined { const ref = useRef<T>();

useEffect(() => { ref.current = value; }, [value]);

return ref.current; }

// Usage function Counter({ count }: { count: number }) { const prevCount = usePrevious(count); // prevCount is the previous value of count }

  1. useAsync

interface AsyncState<T> { data: T | null; loading: boolean; error: Error | null; }

function useAsync<T>(asyncFn: () => Promise<T>, deps: unknown[] = []) { const [state, setState] = useState<AsyncState<T>>({ data: null, loading: true, error: null, });

useEffect(() => { setState(s => ({ ...s, loading: true })); asyncFn() .then(data => setState({ data, loading: false, error: null })) .catch(error => setState({ data: null, loading: false, error })); }, deps);

return state; }

// Usage const { data, loading, error } = useAsync(() => fetchUser(id), [id]);

  1. useCopyToClipboard

function useCopyToClipboard() { const [copiedText, setCopiedText] = useState<string | null>(null);

const copy = async (text: string) => { try { await navigator.clipboard.writeText(text); setCopiedText(text); return true; } catch { setCopiedText(null); return false; } };

return { copiedText, copy }; }

  1. useEventListener

function useEventListener<K extends keyof WindowEventMap>( eventName: K, handler: (event: WindowEventMap[K]) => void, element: Window | HTMLElement = window ) { const savedHandler = useRef(handler);

useEffect(() => { savedHandler.current = handler; }, [handler]);

useEffect(() => { const listener = (event: Event) => savedHandler.current(event as WindowEventMap[K]); element.addEventListener(eventName, listener); return () => element.removeEventListener(eventName, listener); }, [eventName, element]); }

// Usage useEventListener('scroll', () => console.log('scrolled')); useEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); });

  1. useIntersectionObserver

function useIntersectionObserver( ref: RefObject<Element>, options?: IntersectionObserverInit ): boolean { const [isIntersecting, setIsIntersecting] = useState(false);

useEffect(() => { if (!ref.current) return;

const observer = new IntersectionObserver(([entry]) => {
  setIsIntersecting(entry.isIntersecting);
}, options);

observer.observe(ref.current);
return () => observer.disconnect();

}, [ref, options]);

return isIntersecting; }

// Usage - Lazy loading function LazyImage({ src }: { src: string }) { const ref = useRef<HTMLDivElement>(null); const isVisible = useIntersectionObserver(ref, { threshold: 0.1 });

return ( <div ref={ref}> {isVisible && <img src={src} />} </div> ); }

Best Practices

Do Don't

✅ Prefix with use

❌ Name without use

✅ Return stable references ❌ Return new objects each render

✅ Handle cleanup ❌ Forget cleanup in useEffect

✅ Use TypeScript generics ❌ Use any types

References

  • React Hooks Documentation

  • usehooks-ts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

solid

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

reasoning

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

find-skills

No summary provided by upstream source.

Repository SourceNeeds Review