Waarom loading & error states?

Je WorkoutList uit de vorige stap haalt netjes data op. Maar wat ziet de gebruiker terwijl die fetch onderweg is? Niets. Een lege pagina. En als de backend offline is? Ook niets. Of erger: een crash.

Een goede UI laat altijd weten wat er gebeurt:

Drie momenten, drie schermen

  • Bezig met laden → "Laden..."
  • Iets misgegaan → foutmelding tonen
  • Klaar → de echte data tonen

Drie states naast elkaar

Je hebt drie stukjes state nodig in je WorkoutList:

const [workouts, setWorkouts] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
Wat doet elke state?
  • workouts — de array die je toont. Begint leeg.
  • isLoadingtrue tijdens fetch, false daarna. Begint true omdat de fetch direct start.
  • errornull als alles goed gaat, een tekst bij een fout.

WorkoutList uitbreiden

Je vult je bestaande WorkoutList aan met de twee extra states en een try/catch rond de fetch:

// frontend/src/components/WorkoutList.jsx

import { useEffect, useState } from 'react';
import Workout from './Workout';

function WorkoutList() {
  const [workouts, setWorkouts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchWorkouts = async () => {
      try {
        const response = await fetch('http://localhost:4000/api/workouts');

        if (!response.ok) {
          throw new Error('Kon workouts niet ophalen');
        }

        const data = await response.json();
        setWorkouts(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setIsLoading(false);
      }
    };

    fetchWorkouts();
  }, []);

  if (isLoading) {
    return <p>Laden...</p>;
  }

  if (error) {
    return <p className="error">Er ging iets mis: {error}</p>;
  }

  return (
    <div className="workout-list">
      {workouts.length === 0
        ? <p>Geen workouts gevonden.</p>
        : workouts.map(workout => (
          <Workout key={workout._id} workout={workout} />
        ))}
    </div>
  );
}

export default WorkoutList;
Wat is er nieuw?
  • try { ... } catch (err) { ... } finally { ... } — vangt netwerkfouten af én zorgt dat isLoading sowieso op false komt.
  • if (!response.ok) — fetch throwt niet bij een 404 of 500. Je moet zelf checken en een error gooien.
  • setError(err.message) — bewaart de foutmelding zodat je hem in de UI kunt tonen.
  • finally — draait altijd, of het nu goed of fout ging. Perfect om isLoading uit te zetten.

Conditioneel renderen

Onderaan zie je drie return-blokken. React rendert het eerste blok dat van toepassing is en stopt daar. Dat heet early return.

if (isLoading) return <p>Laden...</p>;
if (error)     return <p className="error">{error}</p>;
// alleen hier kom je als alles goed ging
return <div>...</div>;

Volgorde is belangrijk

Eerst isLoading, dan error, dan de echte UI. Andersom kun je de error niet tonen omdat workouts dan al leeg is.

Testen of het werkt

Loading testen

Open DevTools → tab Network → zet Throttling op Slow 3G. Refresh de pagina — je ziet "Laden..." een paar seconden.

Error testen

Stop je backend (Ctrl+C in terminal 1). Refresh de frontend. Je ziet nu de foutmelding in plaats van een lege pagina.

Veelgemaakte fouten

1. isLoading start op false

Dan flashed de gebruiker even "Geen workouts gevonden" voor de echte data binnen is. Start altijd op true.

2. setIsLoading(false) alleen in try

Dan blijft de loading-spinner hangen bij een error. Gebruik finally, of zet hem in beide takken.

3. response.ok niet checken

Een 404 of 500 belandt gewoon in .then(). Check eerst of de response oké is, anders gooi je geen error en zie je nooit wat er fout ging.

4. De error ergens in de console laten staan

Gebruikers openen geen DevTools. Toon de fout altijd in de UI.

Volgende Stap

Lezen werkt netjes. Tijd om gebruikers ook nieuwe data te laten toevoegen.

Formulier Toevoegen (POST) →

Controlled inputs en een POST-request naar je backend