Topic 34 of 54
Building a useFetch Hook
Overview
Data fetching requires managing `data`, `loading`, and `error` states. Writing this three-part state logic in every single component is repetitive and error-prone. A `useFetch` custom hook abstracts all of this boilerplate away, giving you a clean, one-line data fetching solution for your entire app.
Syntax
This hook encapsulates everything: the fetch call, error handling, loading toggles, and cleanup to prevent memory leaks.
The useFetch
jsx
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true; // Prevent state updates if component unmounts
setIsLoading(true);
fetch(url)
.then(res => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
return res.json();
})
.then(json => {
if (isMounted) {
setData(json);
setError(null);
}
})
.catch(err => {
if (isMounted) {
setError(err.message);
setData(null);
}
})
.finally(() => {
if (isMounted) setIsLoading(false);
});
return () => { isMounted = false; };
}, [url]); // Re-fetch if the URL changes
return { data, isLoading, error };
}The component focuses purely on the View layer, completely decoupled from the messy network logic.
Using useFetch in a Component
jsx
import { useFetch } from './hooks/useFetch';
function UserList() {
// Look how incredibly clean this component is now!
const { data, isLoading, error } = useFetch("https://api.example.com/users");
if (isLoading) return <p>Loading users...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}Common Pitfalls
- Forgetting to include the `url` in the dependency array of the internal `useEffect`, meaning the hook wouldn't fetch new data if the parent passed a different URL.
Interview Tips
- Writing a basic `useFetch` hook is a very common live-coding interview task for mid-level React roles. Memorize the `data/isLoading/error` pattern.
Real-World Example
While building your own `useFetch` is a great learning exercise, in a real production app, you would use community-tested libraries.
example
jsx
// In the real world, you rarely write your own useFetch.
// You install libraries like SWR (by Vercel) or React Query (by TanStack).
import useSWR from 'swr';
const fetcher = url => fetch(url).then(res => res.json());
function Profile() {
// SWR gives you caching, automatic retries, and background refetching out of the box
const { data, error, isLoading } = useSWR('/api/user', fetcher);
}