A custom React hook for fetching data with loading and error states.
import { useState, useEffect } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
export function useFetch<T>(
url: string,
options?: RequestInit
): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
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 instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, refetch: fetchData };
}Fetching data is a core part of most React applications. This hook encapsulates the common pattern of loading, error handling, and data management.
import { useState, useEffect } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
export function useFetch<T>(
url: string,
options?: RequestInit
): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
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 instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, refetch: fetchData };
}
data, loading, and error.refetch function to manually trigger a new request.function UserProfile({ userId }: { userId: string }) {
const { data, loading, error, refetch } = useFetch<User>(
`/api/users/${userId}`
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return null;
return (
<div>
<h1>{data.name}</h1>
<button onClick={refetch}>Refresh</button>
</div>
);
}
function CreatePost() {
const { data, loading, error, refetch } = useFetch<Post>(
'/api/posts',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'New Post' }),
}
);
}
react-query or swr.refetch function allows manual refresh without changing the URL.A simple, reusable hook that handles the common data fetching pattern with built-in loading and error states.