Extract data fetching logic into a reusable hook.
Basic useFetch
jsximport { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; async function fetchData() { setLoading(true); setError(null); try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const json = await response.json(); if (!cancelled) { setData(json); } } catch (err) { if (!cancelled) { setError(err); } } finally { if (!cancelled) { setLoading(false); } } } fetchData(); return () => { cancelled = true; }; }, [url]); return { data, loading, error }; }
Usage
jsxfunction UserProfile({ userId }) { const { data: user, loading, error } = useFetch(`/api/users/${userId}`); if (loading) return <Spinner />; if (error) return <Error message={error.message} />; return <Profile user={user} />; }
Adding Refetch Capability
jsxfunction useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const response = await fetch(url); const json = await response.json(); setData(json); } catch (err) { setError(err); } finally { setLoading(false); } }, [url]); useEffect(() => { fetchData(); }, [fetchData]); return { data, loading, error, refetch: fetchData }; } // Usage function UserProfile() { const { data, loading, error, refetch } = useFetch('/api/user'); return ( <div> {/* ... */} <button onClick={refetch}>Refresh</button> </div> ); }
With Options
jsxfunction useFetch(url, options = {}) { const { immediate = true, // Fetch on mount? initialData = null, ...fetchOptions } = options; const [data, setData] = useState(initialData); const [loading, setLoading] = useState(immediate); const [error, setError] = useState(null); const execute = useCallback(async () => { setLoading(true); setError(null); try { const response = await fetch(url, fetchOptions); const json = await response.json(); setData(json); return json; } catch (err) { setError(err); throw err; } finally { setLoading(false); } }, [url, fetchOptions]); useEffect(() => { if (immediate) { execute(); } }, [immediate, execute]); return { data, loading, error, execute }; }