One of the most common patterns in React applications is making API calls and managing the associated loading and error states. Let’s build a custom hook that makes this process cleaner and more reusable.
The Problem
Every time we make an API call, we end up writing similar boilerplate code:
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
The Solution: use Api Hook
Here’s a custom hook that encapsulates this pattern:
import { useState, useEffect, useCallback } from 'react';
const useApi = (url, options = {}) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [url, options]);
useEffect(() => {
if (url) {
fetchData();
}
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
};
export default useApi;
Usage Example
Now using the hook becomes much cleaner:
import useApi from './hooks/useApi';
const UserProfile = ({ userId }) => {
const { data: user, loading, error, refetch } = useApi(
`/api/users/${userId}`
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<button onClick={refetch}>Refresh</button>
</div>
);
};
Key Benefits
- Reusable: Works with any API endpoint
- Clean: Eliminates boilerplate code
- Flexible: Supports custom options and headers
- Reliable: Includes proper error handling and loading states
This pattern has saved me countless hours of repetitive code writing, and I hope it helps you too!