Proper Network Error Handling in React Applications
Handling network errors properly is crucial for creating robust React applications. Here’s how to implement comprehensive error handling for fetch and Axios requests.
Fetch API Error Handling
Basic Error Handling
// ❌ Bad - no error handling
fetch('/api/data')
.then(response => response.json())
.then(data => setData(data));
Proper Implementation
// ✅ Good - complete error handling
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) { // Handle HTTP errors (404, 500, etc.)
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
console.error('Fetch error:', error);
setError(error.message);
// Optionally show user notification
notifyUser('Failed to load data. Please try again.');
}
}
Axios Error Handling
Basic Error Handling
// ❌ Bad - minimal error handling
axios.get('/api/data')
.then(response => setData(response.data));
Proper Implementation
// ✅ Good - comprehensive error handling
async function fetchData() {
try {
const response = await axios.get('/api/data', {
timeout: 5000, // Set timeout
headers: { 'Authorization': `Bearer ${token}` }
});
setData(response.data);
} catch (error) {
if (axios.isAxiosError(error)) {
// Axios-specific error
if (error.response) {
// Server responded with non-2xx status
console.error('Server error:', error.response.status);
setError(`Server error: ${error.response.status}`);
} else if (error.request) {
// Request made but no response
console.error('Network error:', error.message);
setError('Network error - please check your connection');
} else {
// Something else happened
console.error('Request setup error:', error.message);
setError('Failed to send request');
}
} else {
// Non-Axios error
console.error('Unexpected error:', error);
setError('An unexpected error occurred');
}
}
}
Advanced Error Handling Patterns
1. Centralized Error Handler
// errorHandler.js
export function handleNetworkError(error) {
if (error.response) {
switch (error.response.status) {
case 401:
return 'Please login to continue';
case 403:
return 'You don\'t have permission';
case 404:
return 'Resource not found';
case 500:
return 'Server error - please try later';
default:
return `Error: ${error.response.status}`;
}
} else if (error.request) {
return 'Network error - please check your connection';
} else {
return 'An unexpected error occurred';
}
}
// In your component
try {
await axios.get('/api/data');
} catch (error) {
setError(handleNetworkError(error));
}
2. Retry Mechanism
async function fetchWithRetry(url, retries = 3) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(response.statusText);
return await response.json();
} catch (error) {
if (retries <= 0) throw error;
await new Promise(resolve => setTimeout(resolve, 1000));
return fetchWithRetry(url, retries - 1);
}
}
// Usage
fetchWithRetry('/api/data')
.then(data => setData(data))
.catch(error => setError(error.message));
3. Error Boundary Integration
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return <FallbackUI />;
}
return this.props.children;
}
}
// Wrap components that make network requests
<ErrorBoundary>
<DataFetchingComponent />
</ErrorBoundary>
Best Practices
- Always handle both network and HTTP errors (failed requests and 4xx/5xx responses)
- Provide user-friendly messages – don’t show raw error objects
- Implement loading states – show spinners during requests
- Consider retries for transient failures (network blips, 502 errors)
- Log errors to your monitoring service
- Set timeouts to prevent hanging requests
- Cancel requests when components unmount (using AbortController)
Common Mistakes to Avoid
- Only catching network errors but ignoring HTTP error statuses
- Silently failing without user feedback
- Not cleaning up requests when components unmount
- Assuming all errors are the same without proper classification
- Not implementing loading states leaving users guessing