useFetch
A typed custom hook for handling data fetching operations with built-in loading and error states. This hook simplifies the process of making HTTP requests while maintaining proper state management throughout the request lifecycle.
Implementation
interface FetchResponse<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
interface FetchOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: HeadersInit;
body?: BodyInit | null;
}
function useFetch<T>(url: string, options?: FetchOptions): FetchResponse<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url, {
method: options?.method || 'GET',
headers: options?.headers,
body: options?.body,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const jsonData = await response.json();
setData(jsonData as T);
} catch (err) {
setError(err instanceof Error ? err : new Error('An error occurred'));
} finally {
setLoading(false);
}
};
fetchData();
}, [url, options]);
return { data, loading, error };
}
Usage
Fetch and display a list of todos
interface Todo {
id: number;
title: string;
completed: boolean;
}
export default function TodoList() {
const { data, loading, error } = useFetch<Todo[]>(
'https://jsonplaceholder.typicode.com/todos'
);
return (
<div style={{ padding: '20px' }}>
{loading && <p>Loading todos...</p>}
{error && <p>Error: {error.message}</p>}
{data && (
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{data.map(todo => (
<div
key={todo.id}
style={{
padding: '10px',
border: '1px solid #ccc',
borderRadius: '4px'
}}
>
<h3>
{todo.completed ? '✓ ' : '○ '}
{todo.title}
</h3>
</div>
))}
</div>
)}
</div>
);
}
Parameters
urlstringrequired
The URL to fetch data from
optionsFetchOptions
Optional configuration for the fetch request including method, headers, and body
Return Values
dataT | null
The fetched data when the request is successful, null during loading or on error
loadingboolean
Indicates whether a request is currently in progress
errorError | null
Error object if the request failed, null otherwise
Live Playground
Best Practices
- Always provide type parameters for better type safety
- Handle all possible states (loading, error, success) in the UI
- Use appropriate error boundaries for error handling
Performance Considerations
- Requests are triggered on url or options changes
- Consider memoizing options objects to prevent unnecessary re-renders
- Large responses should be handled with pagination