react
useFetch Hook
A lightweight data fetching hook with loading, error states, and refetch capability.
#hooks
#data-fetching
#api
A simple yet powerful hook for data fetching with TypeScript support.
useFetch Hook
import { useState, useEffect, useCallback } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => Promise<void>;
}
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 = 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 json = await response.json();
setData(json);
} catch (e) {
setError(e instanceof Error ? e : new Error('Unknown error'));
} finally {
setLoading(false);
}
}, [url, JSON.stringify(options)]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
export default useFetch;
Usage
interface User {
id: number;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: number }) {
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>
<p>{data.email}</p>
<button onClick={refetch}>Refresh</button>
</div>
);
}
With POST Request
function CreatePost() {
const { data, loading, error } = useFetch<{ id: number }>('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'New Post' }),
});
// ...
}
Enhanced Version with Abort
function useFetch<T>(url: string, options?: RequestInit): UseFetchResult<T> {
const [state, setState] = useState<{
data: T | null;
loading: boolean;
error: Error | null;
}>({ data: null, loading: true, error: null });
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const json = await response.json();
setState({ data: json, loading: false, error: null });
} catch (e) {
if ((e as Error).name !== 'AbortError') {
setState({ data: null, loading: false, error: e as Error });
}
}
};
fetchData();
return () => controller.abort();
}, [url]);
return { ...state, refetch: () => {} };
}
Features:
- Loading and error states
- Generic type support
- Refetch capability
- Request abort on unmount