JAVASCRIPT
Manage Asynchronous Operations and Loading States with `useFetch`
Create a `useFetch` React hook to handle data fetching from APIs, managing loading states, error handling, and the fetched data in a clean, reusable pattern.
import { useState, useEffect, useCallback } from 'react';
function useFetch(url, options = {}) {
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, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}, [url, JSON.stringify(options)]); // Stringify options for deep comparison
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
// Example Usage:
/*
function UserProfile({ userId }) {
const { data: user, loading, error, refetch } = useFetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (loading) return <div>Loading user...</div>;
if (error) return <div>Error: {error.message} <button onClick={refetch}>Retry</button></div>;
if (!user) return <div>No user found.</div>;
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
*/
How it works: The `useFetch` hook abstracts away common patterns for fetching data from an API. It manages `loading`, `error`, and `data` states internally. When the `url` or `options` change, it uses `useCallback` to memoize the `fetchData` function, ensuring efficient re-fetching only when necessary. It returns these states along with a `refetch` function, allowing components to easily display loading indicators, error messages, and the fetched data, while also providing a mechanism to retry the fetch operation.