Wat is Data Fetching?
Data fetching is het ophalen van data van een server/API. In React gebruik je de fetch() functie binnen een useEffect hook.
Typisch patroon:
- Component mount → useEffect
- Fetch data van API
- Zet data in state
- Component rendert opnieuw met data
Basis Fetch met .then()
import { useState, useEffect } from 'react';
const Users = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(data => setUsers(data));
}, []); // Lege array = 1x uitvoeren
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
export default Users;
Stappen:
fetch(url)- Haal data op.then(res => res.json())- Converteer naar JSON.then(data => setUsers(data))- Zet in state
Fetch met async/await (beter!)
Async/await is moderner en leesbaarder dan .then():
import { useState, useEffect } from 'react';
const Users = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
setUsers(data);
};
fetchUsers();
}, []);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
Let op: Je kunt useEffect zelf niet async maken! Maak een aparte async functie binnen useEffect.
Met Loading en Error Handling
import { useState, useEffect } from 'react';
const Users = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Er ging iets mis');
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
Zie het in actie — de drie states
Een API-call kost tijd. In die tijd moet je gebruiker iets zien. Links zie je wat je app-gebruiker ziet, rechts wat er in je state gebeurt. Klik op "Fetch users" en kijk hoe de drie fases voorbij komen.
Wat de gebruiker ziet
Klik op "Fetch users" om data te laden
De state in je code
loading
false
error
null
data
null
Timeline
Klik op "Fetch users" om te starten...
De drie fases die je net zag:
- Loading — request gestart, wachten op antwoord. Toon een spinner of skeleton.
- Success — data binnen. Toon de data.
- Error — er ging iets fout. Toon een nette foutmelding.
Zet de toggle "Laat volgende request falen" aan en fetch opnieuw — zie de error state in actie.
POST Request (data versturen)
Om data naar een API te versturen gebruik je een POST request:
const CreateUser = () => {
const [name, setName] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name })
});
const data = await response.json();
console.log('User created:', data);
setName(''); // Reset form
} catch (error) {
console.error('Error:', error);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Naam"
/>
<button type="submit" disabled={loading}>
{loading ? 'Bezig...' : 'Maak User'}
</button>
</form>
);
};
POST request opties:
method: 'POST'- Type requestheaders- Metadata (bijv. Content-Type)body- Data als JSON string
Praktische Voorbeelden
Single Item Fetchen
const UserDetail = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
const data = await response.json();
setUser(data);
setLoading(false);
};
fetchUser();
}, [userId]); // Re-fetch als userId verandert
if (loading) return <p>Loading...</p>;
if (!user) return <p>User not found</p>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
};
Update (PUT/PATCH)
const updateUser = async (userId, newData) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newData)
});
const data = await response.json();
return data;
};
Delete
const deleteUser = async (userId) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`, {
method: 'DELETE'
});
if (response.ok) {
console.log('User deleted');
}
};
Refetch Button
const Users = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const fetchUsers = async () => {
setLoading(true);
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
setUsers(data);
setLoading(false);
};
useEffect(() => {
fetchUsers();
}, []);
return (
<div>
<button onClick={fetchUsers} disabled={loading}>
{loading ? 'Loading...' : 'Refresh'}
</button>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};