Einführung
Daylight Festival bietet sechs Programmpunkte auf drei Bühnen. Zwei können denselben Titel haben; dein persönlicher Zeitplan muss deshalb die tatsächlichen Identitäten behalten. Du verbindest eine vorbereitete responsive React-Seite mit einer einzigen Auswahl und leitest daraus Karten, Anzahl und Gesamtminuten anhand des aktuellen Programms ab. Das Filtern darf eine frühere Auswahl nicht verlieren.
Du benötigst Kenntnisse über React-Komponenten, JSX, Props, useState, Ereignisse, bedingtes Rendern, map/filter, Listenschlüssel und kontrollierte Eingaben sowie JavaScript-Arrays, Objekte, Funktionen und grundlegendes HTML/CSS. Dieses Lab behandelt abgeleiteten Zustand, die Suche nach aktuellen Datensätzen, unveränderliche Auswahlaktualisierungen, eine Reduktion der Dauer und Hinweise auf fehlende IDs. Es startet unabhängig und benötigt keine Dateien einer früheren VM. Die 55 Minuten sind eine Schätzung des Autors, keine mit Anfängern gemessene Zeit. Alle Programmpunkte und Identitäten sind synthetisch.
Verwende Workspace für das Terminal und Festival für die echten Seitenlinks. Bearbeite in diesem Lab nur /home/labex/project/festival-schedule/work/src/selection.js und src/App.jsx. Erhalte src/settings.js, src/main.jsx, src/conflicts.js, src/storage.js, den vorbereiteten Renderer und die Stile in shared, Study, Abhängigkeiten und Dienstkonfiguration. Konfliktentscheidungen und Browserspeicherung sind hier keine Anforderungen. Das vollständige Study-Beispiel enthält sie als vorbereitetes Verhalten; du implementierst sie im nächsten Lab. Die Beschriftungen der Website bleiben englisch.
Die Auswahl aktuellen Programmpunkten zuordnen
In diesem Schritt verwendest du zuerst ein normales Beispiel und wandelst dann stabile IDs in die tatsächlichen aktuellen Programmdatensätze um.
Wähle in Festival den Link Open Study schedule in a new page. Er öffnet eine echte eigenständige Browserseite außerhalb des eingebetteten Produkt-Tabs. Führe die Website-Aktionen dort aus. Gehe mit Tab zu Add to schedule bei First Light, Garden, 10:00–10:30, und drücke Enter. Gehe weiter zu Paper Signals, Studio, 10:30–11:15, und füge diesen Programmpunkt hinzu. Wähle View my schedule: Die beiden echten Karten und 2 sessions · 75 minutes erscheinen. Der Navigationslink des Tages springt zur Zeitplanüberschrift; Back to the programme führt zum Katalog zurück. Dieses Beispiel hat eine eigene Auswahl und ist weder deine Arbeit noch ein Bewertungsergebnis. Layout, Kartenrückmeldungen und Navigation sind bereits vorbereitet; du baust die Auswahl dahinter.
Schließe Study. Wähle auf der Festival-Einstiegsseite des Produkts Open Work schedule in a new page und lasse die Seite offen. Sie ist unvollständig. Wechsle zum Produkt-Tab Workspace, um Dateien zu bearbeiten. Ein Produkt-Tab-Wechsel ist etwas anderes als ein Website-Link, ein Seitenanker oder die Zurück-Schaltfläche des Browsers. Der vorbereitete Workspace-Befehl öffnet das Arbeitsverzeichnis; der folgende sichtbare Befehl verdeutlicht ebenfalls den Startpfad. cd wechselt das Verzeichnis dieses Terminals, ein absoluter Pfad beginnt mit /, pwd zeigt den aktuellen Pfad und cat zeigt eine kleine Datei unverändert an.
cd /home/labex/project/festival-schedule/work
pwd
cat src/settings.js
Der Katalog verwendet IDs wie D11 und D13. Beide heißen First Light, haben aber unterschiedliche Bühnen und Zeiten. Das vorbereitete settings.initialIDs enthält D11 und RETIRED-07: eine kleine synthetische Anfangsauswahl für dieses Lab, keine Speicherung aus einer früheren VM. D11 existiert noch, RETIRED-07 nicht mehr. Nur ein aktueller Datensatz darf eine Karte erzeugen. Die fehlende ID wird in Schritt 3 ausdrücklich erklärt.
Ein kleiner Tippfehler im Dateinamen lässt sich beheben. Der nächste Befehl schreibt selection absichtlich falsch. Er sollte melden, dass die Datei nicht existiert, und mit einem Status ungleich null enden; er verändert nichts:
cat src/selction.js
Lies den Fehler, vergleiche die Schreibweise und verwende den richtigen Pfad:
cat src/selection.js
Die Datei enthält leere Funktionen. currentRecords soll aus dem aktuellen Katalog anhand der ID auswählen, nicht anhand des Titels oder der Array-Position. filter erzeugt ein neues Array; includes prüft, ob eine ID gewählt wurde. Da Katalog-IDs eindeutig sind, kann eine wiederholte ID keine doppelte Karte erzeugen. Sortiere das neue Array nach numerischer Startzeit und bei Gleichstand nach ASCII-ID. Das || im Vergleich verwendet den ID-Vergleich nur, wenn die Zeitdifferenz null ist. condition ? first : second wählt zwischen zwei Werten. Der ursprüngliche Katalog bleibt erhalten.
Speichere mit einem Here-Dokument eine vollständige Datei. Bei cat > path <<'JS' ersetzt > die Datei, << übergibt die folgenden Zeilen als Eingabe und das zitierte JS verhindert Shell-Erweiterungen im JavaScript. Das abschließende JS steht allein auf einer Zeile. Dies ist ein Bearbeitungsweg im Terminal; alternativ kannst du dieselben Dateien in WebIDE bearbeiten und mit Ctrl+S speichern. Die übrigen drei Funktionen bleiben für die nächsten Schritte unfertig.
cat > src/selection.js <<'JS'
export function currentRecords(ids, catalog) {
return catalog.filter(session => ids.includes(session.id)).sort((a, b) =>
a.start - b.start || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
);
}
export function totalMinutes(records) {
return 0;
}
export function visibleRecords(catalog, stage) {
return catalog;
}
export function missingNotice(ids, catalog) {
return '';
}
JS
Kehre zur eigenständigen Work-Seite zurück und aktualisiere den Browser normal. View my schedule zeigt jetzt die tatsächliche Garden-Karte First Light, 10:00–10:30, mit aktueller Beschreibung. Für RETIRED-07 darf keine Karte erfunden werden. Die Zusammenfassung hat noch ihre Anfangswerte, Add/Remove sind noch nicht verbunden; das erledigt der nächste Schritt. Suche in der Datei den Vergleich mit session.id: Eine andere Katalogreihenfolge darf nicht ändern, welches Objekt D11 bezeichnet.
Zeigt der Browser einen Modul- oder Syntaxfehler, gehe zu Workspace zurück, führe pwd und cat src/selection.js aus und vergleiche Importe, Funktionsnamen, Anführungszeichen und Klammern mit dem vollständigen Block. Speichere die Korrektur unter genau diesem Pfad und aktualisiere Work normal. Setze die VM nicht zurück und installiere keine Pakete neu, um einen Datei- oder JavaScript-Tippfehler zu beheben. Starte die Schrittprüfung, sobald die aktuelle Karte sichtbar ist.
Die Zusammenfassung ableiten und eine einzige Auswahl aktualisieren
In diesem Schritt verbindest du Add und Remove mit einer einzigen ID-Liste und berechnest Anzahl und Dauer.
Beginne in Workspace unter /home/labex/project/festival-schedule/work. Eine separat gespeicherte Anzahl oder Dauer kann nach dem Entfernen einer Karte von der tatsächlichen Auswahl abweichen. Halte selectedIDs als Auswahlzustand und leite die aktuellen Datensätze bei jedem Rendern ab. Ihre Länge liefert die Anzahl. reduce fasst Datensätze zu einem Wert zusammen: Beginne bei null Minuten und addiere jedes end - start. Die vorbereiteten Zeiten sind ganzzahlige Minuten an einem lokalen Datum; weder Date-Parsing noch Zeitzonenumrechnung ist erforderlich. D11 dauert 30 Minuten, D12 45, zusammen also 75. Pausen zwischen Programmpunkten zählen nicht als Teilnahmezeit.
Ersetze selection.js durch dieselbe ID-Suche und die vollständige Reduktion. Filter und Erklärung fehlender IDs bleiben Anfangsfunktionen für Schritt 3.
cat > src/selection.js <<'JS'
export function currentRecords(ids, catalog) {
return catalog.filter(session => ids.includes(session.id)).sort((a, b) =>
a.start - b.start || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
);
}
export function totalMinutes(records) {
return records.reduce((minutes, session) => minutes + session.end - session.start, 0);
}
export function visibleRecords(catalog, stage) {
return catalog;
}
export function missingNotice(ids, catalog) {
return '';
}
JS
Verbinde nun die React-Komponente. useState liefert jeden Zustandswert und die zugehörige Aktualisierungsfunktion. Ausgewählte IDs, sichtbare Bühne und Rückmeldungen haben unterschiedliche Aufgaben; ein Bühnenwechsel darf die ID-Liste nicht ersetzen. Ausgewählte Karten und Zusammenfassung sind berechnete Werte, keine zusätzlichen Zustände.
Die Aktualisierungsfunktion ids => [...ids, id] erhält das aktuelle ID-Array und erzeugt mit Spread ... ein neues. Das Entfernen erzeugt mit filter ein neues Array; es entfernt die passende ID, nicht alle gleichnamigen Programmpunkte. Eine Prüfung bereits ausgewählter IDs verhindert Duplikate. find liefert den tatsächlichen Datensatz der angeklickten ID. feedback ist ein kleines Objekt für den vorbereiteten Renderer: sessionID setzt das Add-Ergebnis neben die zugehörige Karte, text enthält die lesbare Meldung. Der Renderer hält sie sichtbar, ohne den Tastaturfokus in einen Live-Statusbereich zu verschieben.
clock stammt aus dem vorbereiteten View.jsx und formatiert ganzzahlige Minuten für die Anzeige. describe verbindet Titel, Bühne und Zeit, damit gleichnamige Programmpunkte unterscheidbar bleiben. Alle folgenden Komponenten-Props haben eine konkrete Quelle im Zustand oder in abgeleiteten Datensätzen. Ersetze App.jsx vollständig:
cat > src/App.jsx <<'JS'
import React, {useState} from 'react';
import View, {clock} from '../../shared/View.jsx';
import {catalog, settings} from './settings.js';
import {currentRecords, totalMinutes, visibleRecords, missingNotice} from './selection.js';
const describe = session => session.title + ' (' + session.stage + ', ' + clock(session.start) + '-' + clock(session.end) + ')';
export default function App() {
const [selectedIDs, setSelectedIDs] = useState(settings.initialIDs);
const [stage, setStage] = useState('all');
const [feedback, setFeedback] = useState({sessionID: null, text: ''});
const selected = currentRecords(selectedIDs, catalog);
const visible = visibleRecords(catalog, stage);
const minutes = totalMinutes(selected);
function add(id) {
const session = catalog.find(item => item.id === id);
if (!session || selectedIDs.includes(id)) return;
setSelectedIDs(ids => [...ids, id]);
setFeedback({sessionID: id, text: 'Added ' + describe(session) + '.'});
}
function remove(id) {
setSelectedIDs(ids => ids.filter(existing => existing !== id));
setFeedback({sessionID: null, text: ''});
}
function changeStage(value) {
setStage(value);
setFeedback({sessionID: null, text: ''});
}
return <View settings={settings} visible={visible} selected={selected}
count={selected.length} minutes={minutes} selectedIDs={selectedIDs}
stage={stage} onStage={changeStage} onAdd={add} onRemove={remove}
feedback={feedback} missingNotice={missingNotice(selectedIDs, catalog)} />;
}
JS
Kehre zu Work zurück und aktualisiere. Die synthetische Anfangsauswahl ergibt 1 session · 30 minutes und die Karte von Garden First Light. Füge Paper Signals hinzu: Es werden 2 Programmpunkte und 75 Minuten. Füge First Light bei Loft, 12:00–12:30 hinzu: Beide First Light müssen mit unterschiedlichen Bühnen und Zeiten erhalten bleiben, insgesamt 3 Programmpunkte und 105 Minuten. Entferne nur Garden First Light. Paper Signals und die spätere Loft First Light bleiben: 2 Programmpunkte und 75 Minuten. Dies zeigt auch, dass die Dauer Teilnahmezeiten addiert und nicht die Zeitspanne vom ersten Beginn bis zum letzten Ende angibt.
Der bereitgestellte Renderer nutzt jede ID als React-Schlüssel. Schlüssel verfolgen die Identität bei Listenänderungen; sie ersetzen deinen Auswahlzustand nicht. Add wird zu Added, und erneutes Auswählen darf keine weitere Kopie erzeugen. In dieser Einheit verwendet die Bühnenauswahl noch eine Anfangsfunktion; Überschneidungen werden absichtlich noch nicht verhindert. Behaupte nicht, jede mögliche Auswahl sei schon konfliktfrei. Starte die Schrittprüfung, wenn Karten und Zusammenfassung zusammenpassen.
Das Programm filtern, ohne die Auswahl zu verlieren
In diesem Schritt vervollständigst du den Bühnenfilter und erklärst eine nicht mehr verfügbare frühere ID, ohne gültige Programmpunkte zu verlieren.
Beginne in Workspace. visibleRecords filtert nur das Programm-Array nach stage === 'all' oder der passenden Bühne und sortiert das neue Array nach Startzeit und ID. Es bearbeitet niemals selectedIDs. missingNotice prüft, ob einer gewählten ID ein aktueller Datensatz fehlt. some ergibt true, wenn mindestens ein Element eine Bedingung erfüllt; das innere some prüft auf eine passende aktuelle ID, ! bedeutet, dass keine passt. Der Hinweis ist eine normale Rückmeldung, kein Fehler, der den gesamten Zeitplan verwirft. Fehlt nichts, kann die Funktion eine leere Zeichenfolge zurückgeben.
Speichere die vollständige Hilfsdatei:
cat > src/selection.js <<'JS'
export function currentRecords(ids, catalog) {
return catalog.filter(session => ids.includes(session.id)).sort((a, b) =>
a.start - b.start || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
);
}
export function totalMinutes(records) {
return records.reduce((minutes, session) => minutes + session.end - session.start, 0);
}
export function visibleRecords(catalog, stage) {
return catalog.filter(session => stage === 'all' || session.stage === stage).sort((a, b) =>
a.start - b.start || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
);
}
export function missingNotice(ids, catalog) {
const missing = ids.some(id => !catalog.some(session => session.id === id));
return missing ? 'Some earlier choices are no longer listed. Your available sessions are kept.' : '';
}
JS
Kehre zu Work zurück und aktualisiere. Das vorbereitete D11 bleibt eine echte Karte mit 30 Minuten. Ein sichtbarer Hinweis erklärt, dass manche früheren Auswahlen nicht mehr angeboten werden; er erfindet keinen veralteten Titel für RETIRED-07 und löscht D11 nicht. Die Quelle ist weiterhin das öffentliche settings.initialIDs dieser unabhängigen Einheit, nicht localStorage. Füge Paper Signals erneut hinzu, um zwei Auswahlen mit zusammen 75 Minuten zu erhalten.
Setze Stage auf Garden. Das Programm enthält nun die Garden-Termine, während View my schedule weiterhin Garden First Light und Studio Paper Signals mit 2 Programmpunkten und 75 Minuten enthält. Stelle Stage wieder auf All stages: Paper Signals ist weiterhin Added. Entferne es über My schedule und prüfe, dass nur diese ID die Liste verlässt. Der Hinweis auf die entfernte synthetische ID darf bleiben; sie trägt weder Karte noch Minuten bei. Beim normalen Aktualisieren kehrt diese erste Einheit zu ihren vorbereiteten Anfangs-IDs zurück, da noch keine Persistenz implementiert ist.
Probiere Tab und Enter an Bühnenauswahl, Navigationslinks und verfügbaren Add/Remove-Schaltflächen. Bei schmaler Anzeige bleibt View my schedule in der Tagesnavigation und Add-Rückmeldungen erscheinen neben der angeklickten Karte. Vergleiche Titel, Bühne, Zeit und Beschreibung, nicht nur die Anzahl. Erhalte den sichtbaren Fokusindikator und die sinnvollen Beschriftungen des vorbereiteten Renderers. Starte die letzte Schrittprüfung. Du hast nun eine konsistente Auswahl und eine Ansicht aktueller Daten; das nächste unabhängige Lab ergänzt Zeitkonflikte und Browserwiederherstellung.
Um die folgende Referenz nachzustellen, kehre zur eigenständigen Work-Seite zurück und aktualisiere normal. Dies stellt die unabhängige Anfangsauswahl Garden First Light und den Hinweis auf die entfernte ID wieder her. Setze Stage auf All stages. Ist Paper Signals noch nicht Added, wähle seine Schaltfläche Add to schedule. Vergleiche dann die beiden aktuellen Karten und die Zusammenfassung mit 75 Minuten mit dem getrennten Hinweis zur nicht verfügbaren Auswahl. Die entfernte ID erzeugt weder Karte noch Minuten.

Zusammenfassung
Du hast eine React-Auswahl mit stabilen IDs aufgebaut, sie anhand des aktuellen Katalogs aufgelöst und ihre Karten, Anzahl und geplanten Minuten abgeleitet. Gleichnamige Datensätze blieben unterscheidbar, Filter erhielten die Auswahl und eine entfernte synthetische ID wurde verständlich erklärt, ohne gültige Programmpunkte zu verlieren.
Diese Einheit hält die Auswahl bewusst nur im Arbeitsspeicher und verhindert noch keine Überschneidungen. Das nächste Lab startet unabhängig mit vorbereitetem Auswahlverhalten und ergänzt Konfliktentscheidungen und Browserspeicherung innerhalb desselben Ursprungs.
