Zum Inhalt springen

Aufgabe 07-4a - State-Training

Zu Zen-Modus wechseln
  • Du entscheidest begründet, welche Werte State sind und welche beim Rendern berechnet werden.
  • Du aktualisierst State nur über den Setter, mit der Funktionsform, wenn der neue Wert vom alten abhängt, und Arrays immutabel.
  • Du baust ein kontrolliertes Formularfeld mit live abgeleiteter Anzeige.
  • Du hebst gemeinsamen State in den nächstgemeinsamen Vorfahren und meldest Änderungen über Callback-Props.

Ein Getränkeautomat merkt sich, wie viel Geld eingeworfen wurde, sonst könnte er nicht entscheiden, ob er etwas herausgibt. Genau diese Frage stellt sich bei jeder Oberfläche: Was muss sich das Programm merken, und was kann es jederzeit ausrechnen? Wer sie falsch beantwortet, baut Anzeigen, die irgendwann nicht mehr zusammenpassen.

An vier kleinen Komponenten trainierst du den Umgang mit State (siehe Kapitel React-Grundlagen, Einheiten 7.4 und 7.5). Die Stufe Wesentlich_überwiegend passt nach Einheit 7.4 (State und Render-Kreislauf), die Stufe Wesentlich_vollständig nach Einheit 7.5 (Datenfluss und Formulare); die Stufen PlusPlus und MandalorianPlus heben State nach oben.

Projektordner anlegen siehe https://qiz.at/projektordner

  • Lies vorher kurz https://qiz.at/assignmentsabgabe durch.

  • Nur vollständige Abgaben (siehe https://qiz.at/rules) können positiv bewertet werden.

  • Abgabe- und Testdetails siehe https://qiz.at/assignmentsabgabe

  • Die Übung liegt als Unterordner uebung-07-4a in deinem Jahres-Repository SJ26_27_4AHITM_SEW_<Benutzername>_exercises, angelegt wie uebung-07-1a mit npm create vite@latest uebung-07-4a -- --template react-ts. Du arbeitest allein.

  • Leg die Komponente src/components/StateTraining.tsx an und render sie aus App.tsx; sie ist die Klammer um die Übungskomponenten. Jede Übungskomponente kommt in eine eigene Datei unter src/components/ und wird in StateTraining untereinander gerendert:

    import { Thumbs } from "./Thumbs";
    export function StateTraining() {
    return (
    <section>
    <Thumbs />
    {/* the other components get added here as you build them */}
    </section>
    );
    }
  • Schriftliche Antworten stehen als Kommentar im Code, die State-Tabelle in der README.md des Projekts. npm run build muss fehlerfrei durchlaufen; node_modules/ kommt nicht ins Repository.

  • Thumbs, ein Daumen-hoch/-runter-Zähler: zwei Buttons und dazwischen die Differenz.
    • Entscheide vor dem Code: ein State (die Differenz) oder zwei (up und down)? Schreib Wahl und Begründung als Kommentar über die Komponente.
    • Die Klicks verwenden die Funktionsform des Setters: setUp((current) => current + 1).
    • Die Differenz wird bei zwei States berechnet (up - down), nicht gespeichert.
  • Pflicht-Experiment: Schreib in den Klick-Handler von Thumbs direkt nach dem Setter ein console.log(up). Beobachte den Wert in der Konsole und erklär ihn in einem Kommentarsatz.
  • DicePair, zwei Würfel und ein “Würfeln”-Button.
    • Zwei States für die Augenzahlen (1 bis 6); der Button setzt beide neu (Math.floor(Math.random() * 6) + 1).
    • Bei einem Pasch erscheint “Pasch!”. Diese Meldung ist kein eigener State, sondern wird aus den beiden Würfelwerten berechnet.
    • Begründe als Kommentar, welcher Fehler bei einem zusätzlichen isDouble-State entstehen könnte.

Tipp: Was müsstest du ändern, wenn später “wie viele Stimmen insgesamt?” dazukommt? Das ist ein Argument für zwei States. “Rendered more hooks than during the previous render” heißt: Ein useState steht in einem if oder einer Schleife.

Vollständig, wenn:

  • npm run build ohne TypeScript-Fehler durchläuft und beide Komponenten über StateTraining bedienbar sind.
  • jeder useState-Aufruf auf oberster Ebene der Komponente steht und Änderungen nur über Setter laufen.
  • Differenz und Pasch-Meldung beim Rendern berechnet werden und keinen eigenen State haben.
  • das Experiment zum Setter-Wert durchgeführt und richtig erklärt ist, ebenso die isDouble-Frage.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • CharacterCounter, ein Textfeld mit Live-Zähler wie bei einer Kurznachricht.
    • Ein <textarea> als controlled component: value aus dem State, onChange schreibt jede Eingabe zurück.
    • Darunter live “42 / 280 Zeichen”; die Länge ist text.length, kein eigener State.
    • Ab 260 Zeichen bekommt die Anzeige eine Warnfarbe über eine abgeleitete CSS-Klasse (className={text.length > 260 ? "warn" : ""}), ohne document.querySelector.
  • ShoppingList: Ein Eingabefeld plus “Hinzufügen”-Button hängt einen Eintrag an, ein Klick auf einen Eintrag entfernt ihn.
    • Zwei States: der Text des Eingabefelds (string) und die Einträge (string[]). Füg immutabel hinzu ([...items, newItem]) und entferne immutabel (items.filter(...)).
    • Pflicht-Experiment: Bau das Hinzufügen absichtlich einmal mit items.push(newItem). Beobachte, dass die Anzeige sich nicht aktualisiert, und erklär im Kommentar, warum React die Änderung nicht bemerkt. Danach zurück zur korrekten Version.
    • Der Button ist deaktiviert, solange das Eingabefeld leer ist (disabled={text.trim() === ""}).
  • Erstell in der README.md eine State-Tabelle für alle vier Komponenten: Variable, echter State oder abgeleitet, Begründung in Stichworten.

Tipp: Ein value ohne onChange friert das Feld ein, und React warnt in der Konsole. disabled="false" ist ein nicht leerer String und damit wahr; der Wert gehört in geschweifte Klammern. Prüfkriterium für echten State: Er entsteht nur durch eine Benutzeraktion und lässt sich aus nichts anderem berechnen.

Vollständig, wenn:

  • alle vier Komponenten über StateTraining erreichbar und bedienbar sind.
  • das Textfeld kontrolliert ist und Zeichenzahl, Warnklasse und disabled beim Rendern berechnet werden.
  • Arrays nur immutabel ersetzt werden, nie mit push oder splice, und das push-Experiment richtig erklärt ist.
  • die State-Tabelle alle vier Komponenten abdeckt und jede Einstufung begründet.

Wie unter Allgemein, dazu ein Screenshot der laufenden Seite inklusive Browser-Adresszeile.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Über allen vier Komponenten soll eine Leiste “Interaktionen gesamt: n” jede Aktion in jeder Komponente mitzählen. Entwirf zuerst auf Papier: Wo muss dieser Zähler leben, und warum kann er in keiner der vier Komponenten selbst wohnen? Notier die Antwort in Stichworten als Kommentar.
  • Setz das Muster State heben um: Der Zähler-State wandert in StateTraining. Jede der vier Komponenten bekommt eine Callback-Prop onInteraction: () => void und ruft sie bei jeder Aktion auf; kein Kind kennt den Zähler selbst.

Tipp: onInteraction() gehört in den Handler, nicht in den Rumpf der Komponente; dort liefe es bei jedem Rendern.

Vollständig, wenn:

  • der Zähler in StateTraining wohnt und alle vier Kinder über eine Callback-Prop melden.
  • kein Kind den Zähler kennt und die Entwurfsbegründung notiert ist.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Die Leiste zeigt zusätzlich, welche Komponente zuletzt benutzt wurde. Erweitere die Signatur zu onInteraction(source: string); jedes Kind meldet seinen Namen mit.
  • Begründe in zwei Sätzen, warum die Alternative, dass jedes Kind direkt in eine globale Variable schreibt, den Einweg-Datenfluss von React zerstören würde.
  • Beurteile in zwei Sätzen: Nach welcher Regel entscheidest du ab jetzt, auf welcher Ebene der Hierarchie ein State wohnt?

Tipp: Denk bei der globalen Variable an den Render-Kreislauf aus Einheit 7.4: Was löst eine Änderung an einer gewöhnlichen Variable aus?

Vollständig, wenn:

  • die Leiste Gesamtzahl und zuletzt benutzte Komponente korrekt anzeigt.
  • die Begründung zur globalen Variable den fehlenden Render-Auslöser oder den verlorenen Einweg-Datenfluss benennt.
  • die Hierarchie-Regel in eigenen Worten formuliert ist.

Wie unter Allgemein.

  1. Woran erkennst du, dass ein Wert kein State sein sollte?
  2. Warum bewirkt items.push(...) keine Aktualisierung der Anzeige?
  3. Was ist der Vorteil der Setter-Funktionsform setCount((c) => c + 1)?
  4. Warum zeigt ein console.log direkt nach dem Setter noch den alten Wert?
  5. Was bedeutet “Props runter, Events rauf” konkret in der Stufe PlusPlus?
  6. Welche zwei Wahrheiten können bei einem redundanten isDouble-State auseinanderlaufen?

Durchschnittlich bis Wesentlich_vollständig: ca. 100 min

  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules