Hint
Functions starting with "use" that call other hooks — extract reusable stateful logic
Custom hooks extract stateful logic from components into reusable functions. They must start with use.
// Custom hook — useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch(url)
.then(r => r.json())
.then(d => { if (!cancelled) setData(d); })
.catch(e => { if (!cancelled) setError(e); })
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; }; // cleanup race condition
}, [url]);
return { data, loading, error };
}
// Usage — clean component
function UserProfile({ id }) {
const { data: user, loading } = useFetch(`/api/users/${id}`);
if (loading) return ;
return {user.name}
;
}
Common custom hook patterns:
useLocalStorage(key, initial) — sync state with localStorageuseDebounce(value, delay) — debounce a valueuseOnClickOutside(ref, handler) — close dropdownsusePrevious(value) — track previous render's valueuseIntersectionObserver(ref) — lazy loading / infinite scroll