Waarom Loading States & Error Handling?
Wanneer je data ophaalt van een API, duurt het even voordat de data er is. Gebruikers willen feedback krijgen over wat er gebeurt.
Slechte user experience (zonder loading):
- Lege pagina zonder uitleg
- Gebruiker weet niet of het werkt
- Crashes als er iets mis gaat
Goede user experience (met loading):
- "Loading..." bericht tijdens laden
- Duidelijke foutmeldingen als iets mis gaat
- Professionele uitstraling
Het 3 States Pattern
Voor elke data fetch heb je meestal 3 states nodig:
const [data, setData] = useState([]); // De data zelf
const [loading, setLoading] = useState(true); // Is het aan het laden?
const [error, setError] = useState(null); // Is er een fout?
Flow:
loading = true→ Toon "Loading..."- Fetch data van API
- Success? →
loading = false,data = result - Error? →
loading = false,error = message
Volledig Voorbeeld
import { useState, useEffect } from 'react';
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true); // Start loading
setError(null); // Reset error
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
if (!response.ok) {
throw new Error('Er is iets misgegaan');
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false); // Stop loading (altijd)
}
};
fetchUsers();
}, []);
// Loading state
if (loading) {
return <p>Loading users...</p>;
}
// Error state
if (error) {
return <p style={{ color: 'red' }}>Error: {error}</p>;
}
// Success state
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
export default UserList;
Stappen uitleg:
try- Probeer data te fetchencatch- Vang fouten op en zet in statefinally- Stop loading (gebeurt altijd!)- Render based op state: loading → error → success
Andere Patterns
Inline Loading & Error
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// ... fetch logic
return (
<div>
{loading && <p>Loading...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
{!loading && !error && (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
);
};
Loading Spinner Component
const Spinner = () => (
<div style={{ textAlign: 'center', padding: '20px' }}>
<div className="spinner"></div>
<p>Loading...</p>
</div>
);
// Gebruik:
if (loading) return <Spinner />;
Error Component
const ErrorMessage = ({ message, onRetry }) => (
<div style={{ color: 'red', padding: '20px' }}>
<h3>Oeps! Er ging iets mis</h3>
<p>{message}</p>
{onRetry && (
<button onClick={onRetry}>Probeer opnieuw</button>
)}
</div>
);
// Gebruik:
if (error) {
return <ErrorMessage message={error} onRetry={fetchUsers} />;
}
Met Retry Functie
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchUsers = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch('https://api.example.com/users');
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) {
return (
<div>
<p>Error: {error}</p>
<button onClick={fetchUsers}>Probeer opnieuw</button>
</div>
);
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
Tips & Best Practices
- Altijd finally gebruiken - Zo wordt loading altijd gestopt
- Reset error bij nieuwe fetch - Voorkom oude foutmeldingen
- Specifieke foutmeldingen - "Network error" ipv "Something went wrong"
- Retry optie bij errors - Geef gebruiker de mogelijkheid het opnieuw te proberen
- Loading op false starten - Als data al lokaal beschikbaar is
Veelgemaakte Fouten
Fout - Loading niet stoppen bij error:
try {
const data = await fetch(url);
setLoading(false);
} catch (err) {
setError(err.message);
// Loading blijft true!
}
Goed - Gebruik finally:
try {
const data = await fetch(url);
} catch (err) {
setError(err.message);
} finally {
setLoading(false); // Altijd uitgevoerd
}
Fout - Geen error handling:
const data = await fetch(url);
// Als fetch faalt, crasht de app!
Goed - Altijd try/catch:
try {
const data = await fetch(url);
} catch (err) {
setError(err.message);
}