Zum Inhalt springen

Aufgabe 13 - Quiz reloaded

Zu Zen-Modus wechseln

Die erste vollständige React-Anwendung: ein Multiple-Choice-Quiz mit Phasen, Punktestand und Auswertung. Die Übung führt alles zusammen, was die React-Wochen aufgebaut haben (State modellieren, Events, bedingtes Rendern), und ergänzt mit einem eigenen Hook die Hook-Familie um ein selbstgebautes Mitglied (siehe Kapitel React-Grundlagen).

  • Kapitel React-Grundlagen vollständig; Aufgaben 11 und 12.
  • Das Next.js-Projekt aus Aufgabe 11/12 (oder ein frisches über npx create-next-app@latest, siehe Aufgabe 11); ein Vorgriff auf Kapitel 8. Next.js I - Struktur und Routing. Zum Vergleich in Teil D wird eine imperative Vanilla-JavaScript-Fassung desselben Quiz ausgeteilt.
  • Sie modellieren den Zustand einer kleinen Anwendung vollständig vor der Implementierung.
  • Sie setzen Ablauflogik ausschließlich über State und bedingtes Rendern um.
  • Sie kapseln wiederverwendbare Logik in einem eigenen Hook.
  • Reproduktion: das Zustandsmodell nach dem Kapitelmuster aufstellen (Teil A).
  • Reorganisation und Transfer: Modell und Muster in eine lauffähige Anwendung überführen (Teile B und C).
  • Reflexion, Problemlösung und Urteilsbildung: deklarativen und imperativen Ansatz vergleichen und die Unterschiede bewerten (Teil D).

Die Übung ist auf etwa zwei Stunden ausgelegt. Teil D ist der Expertenteil.

So richten Sie die Quiz-Seite ein (einmal am Anfang, dann geht es los):

  1. Legen Sie im Projekt den Ordner app/quiz/ an und darin eine Datei page.tsx. Diese Seite erreichen Sie im Browser unter http://localhost:3000/quiz, sobald der Entwicklungsserver läuft (npm run dev).
  2. Die Quiz-Komponente schreiben Sie in eine eigene Datei, etwa app/quiz/Quiz.tsx. Weil das Quiz useState verwendet, braucht diese Datei ganz oben die Zeile "use client"; (sonst läuft sie als Server-Komponente und useState bricht ab).
  3. In page.tsx importieren Sie die Komponente und übergeben ihr die Fragen. Die Fragendatei lib/questions.ts legen Sie erst in Teil B an; bis dahin genügt eine kurze Platzhalterliste direkt in page.tsx, damit die Seite anzeigbar ist:
import { Quiz } from "./Quiz";
// import { questions } from "@/lib/questions"; // wire this up in part B
const questions = [
{ text: "2 + 2 = ?", answers: ["3", "4", "5"], correctIndex: 1 },
];
export default function QuizPage() {
return (
<main>
<Quiz questions={questions} />
</main>
);
}

In Teil B verschieben Sie die Fragen dann nach lib/questions.ts und ersetzen die Platzhalterliste durch den auskommentierten Import. Bauen Sie in der Reihenfolge der Teile: erst das Zustandsmodell auf Papier (Teil A), dann die laufende Anwendung (Teil B), dann den Hook (Teil C).

  1. Bestimmen Sie vor der ersten Codezeile die State-Variablen des Quiz. Kandidaten: Nummer der aktuellen Frage, Punktestand, gewählte Antwort, Phase als Union Type "running" | "feedback" | "finished".
  2. Prüfen Sie jede Variable gegen die Regel aus Aufgabe 12: Ist sie echter State oder ableitbar? (Die Anzeige “Frage 3 von 8” etwa ist ableitbar.)
  3. Skizzieren Sie den Ablauf als Zustandsdiagramm: Phasen als Zustände, Benutzeraktionen als Übergänge (Antwort geklickt, Weiter, Nochmal).
  1. Typisierte Fragenstruktur (interface Question mit Text, drei Antworten, Index der richtigen), mindestens acht Fragen in lib/questions.ts.
  2. Ablauf: eine Frage mit drei Antwort-Buttons; nach dem Klick Phase feedback (richtig grün, falsch rot, richtige Antwort markiert; Buttons deaktiviert), Button “Weiter”; nach der letzten Frage Phase finished mit Prozentwert, Kommentar und “Nochmal”.
  3. Verbindliche Regel: kein direkter DOM-Zugriff (document. ist verboten); jede Ansicht folgt aus Phase und State über bedingtes Rendern.
  4. Fortschrittsanzeige und Punktestand sind immer aktuell, ohne dass irgendwo “aktualisiert” wird; halten Sie im Kommentar fest, warum das im deklarativen Modell automatisch gilt.
  1. Der beste Punktestand soll Neustarts der Seite überleben: Schreiben Sie den Hook useLocalStorage(key, initial) nach dem Muster des Kapitels und verwenden Sie ihn für ein “Best: n %” in der Auswertung.
  2. Der Hook wohnt in lib/use-local-storage.ts und ist quizfrei formuliert (keine Quiz-Begriffe): Er muss unverändert auch in einer anderen Anwendung funktionieren.
  3. Beachten Sie die Client-Grenze: localStorage existiert nur im Browser; die betroffene Komponente braucht "use client".
  1. Lesen Sie die ausgeteilte imperative Vanilla-JS-Fassung desselben Quiz (direkte DOM-Manipulation, Event-Listener, manuelle Anzeigen-Updates) und bringen Sie sie lokal zum Laufen.
  2. Bauen Sie in beide Fassungen dieselbe kleine Erweiterung ein: Nach einer falschen Antwort zeigt eine Zeile “davon richtig: k von n”. Protokollieren Sie je Fassung, welche Stellen Sie anfassen mussten.
  3. Schreiben Sie den Vergleich (fünf bis acht Sätze): Wo steckt im imperativen Ansatz die meiste Arbeit? Welche Fehlerklasse verschwindet deklarativ (Stichwort: vergessene Anzeige-Updates)? Und ehrlich: Gibt es etwas, das imperativ einfacher war?
  4. Zusatz: Provozieren Sie in der React-Fassung einen Key-Fehler (Antworten-Liste mit Index als Key rendern und die Antworten mischen) und dokumentieren Sie den sichtbaren Effekt.
  1. Warum ist die Phase als Union Type besser modelliert als mit zwei Booleans (isFinished, showFeedback)?
  2. Welche Werte des Quiz sind abgeleitet und dürfen keinen eigenen State haben?
  3. Warum sind die Antwort-Buttons in der Feedback-Phase deaktiviert, und wie erreicht man das deklarativ?
  4. Warum braucht die Komponente mit useLocalStorage die Direktive "use client"?
  5. Welche Fehlerklasse des imperativen Ansatzes verhindert React strukturell?

Projekt als Git-Repository, das Zustandsdiagramm, das Erweiterungsprotokoll und der schriftliche Vergleich aus Teil D.