Zum Inhalt
wannacode

Bedingungen: leer, Fehler, lädt

Kurz gesagt: Bedingtes Rendern zeigt nur den Teil des Markups, der zu den Daten passt (lädt, Fehler, leer oder die Liste); in React mit && und dem ternären Operator.

Problem

Lädt, Fehler und die Liste gleichzeitig

Der Katalog ist geladen. Unter der Überschrift steht aber „Wird geladen…“, dann „Die Produkte wurden nicht geladen.“, dann „In dieser Kategorie gibt es keine Produkte.“, und erst danach kommen die Produkte.

React

Die Komponente Catalog bekommt status und die Produkte als Props. Aber sie schaut nicht darauf: Sie zeichnet immer alle vier Teile. Der Käufer weiß nicht, was er glauben soll, und ein Screenreader liest sowohl den Fehler als auch die Produkte vor.

Die Idee

Der Teil, der zu den Daten passt

Der Katalog hat vier Zustände: lädt, Fehler, leer und bereit. Nur der Teil für den aktuellen soll auf dem Bildschirm sein. Das ist bedingtes Rendern: Die Komponente entscheidet anhand der Daten, was sie zurückgibt.

JSX hat drei Wege, je nachdem, wie viel sich ändert:

// Eine ganz andere Ansicht: if und ein frühes return
if (status === 'loading') return <p role="status">Wird geladen…</p>;
// Eins von zwei: ? :
<p>{product.stock === 0 ? 'Ausverkauft' : 'Verfügbar'}</p>
// Etwas oder nichts: &&
{status === 'error' && <p role="alert">Die Produkte wurden nicht geladen.</p>}

if ist gewöhnliches JavaScript: Die Komponente ist eine Funktion und kann verschiedenes JSX zurückgeben. ? : und && sind Ausdrücke, deshalb können sie in JSX stehen, in {}.

Die beiden Bibliotheken lösen dasselbe auf ihre Weise: React mit JavaScript um das Markup herum, Vue mit Attributen darin. Das Ergebnis ist dasselbe: Das Element einer falschen Bedingung ist nicht im DOM. Es ist nicht versteckt, es existiert nicht.

Hier wird status als Prop übergeben. Wie der Katalog ihn beim Laden aus der API selbst ändert, steht im Modul „Effekte, Daten und Formulare“.

Sieh es dir an

Sieh, welcher Zweig hineinkommt

Ein Wert geht in die Komponente, und nur ein Zweig kommt ins DOM. Achte bei jeder Änderung von status auf die Spalte „DOM“.

function Catalog({ status, products }) {
if (status === 'loading') return <p>Wird geladen…</p>;
if (status === 'error') return <p>Fehler</p>;
if (status === 'empty') return <p>Keine Produkte</p>;
return <ul>…</ul>;
}
// im DOM, aber versteckt
<p hidden={status !== 'loading'}>Wird geladen…</p>
Schritt für Schritt, in Worten
  1. status kommt als Prop in die Komponente. Er ist 'loading': Das erste if passt, und die Komponente gibt nur <p>Wird geladen…</p> zurück. Nur das kommt ins DOM.
  2. status wird 'error'. Die Bibliothek zeichnet neu: Jetzt passt der zweite Zweig. <p>Wird geladen…</p> verlässt das DOM, und die Fehlermeldung kommt hinein.
  3. 'empty': nur die Meldung, dass die Kategorie leer ist.
  4. 'ready': Keines der if passt, und die Komponente kommt beim letzten return an: der Liste. Im DOM ist immer ein Zweig.
  5. Der andere Weg: Das Element wird immer gezeichnet, und das Attribut hidden entscheidet, ob man es sieht. Bei loading ist hidden false, und der Text ist sichtbar.
  6. status wird 'ready'. Das Element bleibt mit allem, was darin ist, im DOM und bekommt nur display: none. Niemand sieht es, und ein Screenreader überspringt es.
  7. Eine Bedingung mit if, ? : oder && entfernt das Element aus dem DOM. hidden versteckt es nur. Entfernen ist der Normalfall; Verstecken ist für etwas, das oft umgeschaltet wird oder das Eingetippte behalten muss.

Der erste Teil ist bedingtes Rendern: Bei einem neuen Wert verlässt das alte Element das DOM, und das neue kommt hinein. Der zweite Teil ist Verstecken: Das Element ist die ganze Zeit im DOM und wird nur gezeigt oder versteckt. Darum geht es am Ende der Lektion.

Probier es aus

Die vier Zustände

Catalog zeichnet alles auf einmal. Baue es so um, dass jeder Abschnitt nur den Teil seines Zustands hat: „Wird geladen…“ bei lädt, den Fehler bei Fehler, „In dieser Kategorie gibt es keine Produkte.“ bei leer und die Liste bei bereit. Die anderen Teile dürfen nicht auf der Seite sein.

React

Probier es aus

Ergänze die Lücke

„Wird geladen…“ soll nur erscheinen, solange status loading ist.

{status === 'loading'  <p role="status">Wird geladen…</p>}

Die Idee

Ausverkauft: ein Abzeichen und ein deaktivierter Button

Eine Bedingung gibt es nicht nur für ganze Teile. In der Produktkarte ändert sie einen Text und ein Attribut:

{product.stock === 0 ? (
<p className="badge">Ausverkauft</p>
) : (
<p>Nur noch {product.stock} übrig</p>
)}
<button type="button" disabled={product.stock === 0}>Hinzufügen</button>

disabled={true} setzt das Attribut disabled, und disabled={false} entfernt es.

Ein deaktivierter Button lässt sich nicht drücken, bekommt mit Tab keinen Fokus, und ein Screenreader sagt, dass er nicht verfügbar ist. Das Abzeichen sagt, warum. Ein Button, den man drücken kann und der dann „nicht auf Lager“ sagt, ist schlechter: Der Käufer erfährt es erst nach dem Klick.

Probier es aus

Ausverkauft

Sorge dafür, dass ProductCard „Ausverkauft“ zeigt und den Button „Hinzufügen“ deaktiviert, wenn stock 0 ist. Sonst zeigt sie „Nur noch 5 übrig“, und der Button funktioniert. Der Käse ist ausverkauft.

React

Die Idee

Die Null auf dem Bildschirm

&& gibt nicht true oder false zurück. Es gibt die linke Seite zurück, wenn sie falsy ist, und die rechte Seite, wenn sie truthy ist. React zeichnet Zahlen, auch die 0, und zeichnet false, null und undefined nicht. Also:

{items.length && <p>Im Warenkorb: {items.length}</p>} // leerer Warenkorb: 0
{items.length > 0 && <p>Im Warenkorb: {items.length}</p>} // leerer Warenkorb: nichts

Setz vor && einen Vergleich, der true oder false ergibt: > 0, === 0, !== null. Ein anderer Weg ist ? : mit null für „nichts“.

Probier es aus

Finde den Fehler

Bei einem leeren Warenkorb steht in der oberen Ecke „0“.

Wähle die Zeile mit dem Fehler.

Die Idee

Entfernt oder versteckt

Manchmal muss ein Element im DOM bleiben und nur nicht zu sehen sein. Hier haben beide Panels ein Feld für eine Notiz. Öffne beide, tipp etwas ein, schließ sie und öffne sie wieder.

React
Im ersten Abschnitt nutzt das Panel {removedOpen && …}: Geschlossen ist es weg, und geöffnet ist es neu, mit leerem Feld. Im zweiten nutzt es das Attribut hidden={!hiddenOpen}: Es bleibt mit display: none im DOM und behält das Eingetippte.

Entfernen ist der Normalfall: weniger Elemente auf der Seite, nichts Verstecktes für einen Screenreader. Verstecken ist für Teile, die oft geöffnet und geschlossen werden oder behalten müssen, was darin eingegeben wurde.

Genauer, für Neugierige

hidden und CSS. hidden wirkt über display: none aus den Stilen des Browsers. Gibt eine CSS-Regel dem Element display: flex, gewinnt die Regel, und das Element ist trotzdem sichtbar. Dann versteckst du es mit einer Klasse oder mit style={{ display: 'none' }}.

Gleiche Stelle, gleiches Element. Bei {isSearch ? <input placeholder="Suchen" /> : <input placeholder="Code" />} ist jeder Zweig ein <input> an derselben Stelle. React behält das Element und ändert nur placeholder, und das Eingetippte bleibt. Brauchst du zwei verschiedene Elemente, gib ihnen verschiedene keys.

return null. Eine Komponente, die nichts zeichnen soll, gibt null zurück.

Klassenkomponenten in altem Code machen dasselbe in der Methode render().

aria-hidden ist kein Verstecken. aria-hidden="true" versteckt das Element nur vor einem Screenreader, und es bleibt auf dem Bildschirm. Vor allen zu verstecken geht mit display: none: hidden, v-show oder Entfernen.

Übung

Der Warenkorb

App zeigt drei Warenkörbe: leer, mit Produkten und mit einem ausverkauften Produkt. Baue Cart so, dass der leere „Der Warenkorb ist leer.“ sagt, ohne Liste und ohne Summe, und der Button „Bestellen“ deaktiviert ist. Der volle zeigt die Zeilen, „Gesamt: 4,00 €“ und einen funktionierenden Button. Das ausverkaufte Produkt hat ein Abzeichen „Ausverkauft“, über dem Button steht „Entferne die ausverkauften Produkte, um zu bestellen.“ (Rolle alert), und der Button ist deaktiviert.

React

Test

Teste dich

1. Die Bedingung für „Wird geladen…“ ist falsch. Was steht an ihrer Stelle im DOM?
2. Der Warenkorb ist leer. Was zeichnet {items.length && <p>Im Warenkorb: {items.length}</p>}?
3. Was passt am besten, damit die Komponente nur „Wird geladen…“ zeigt, solange status loading ist?
4. Ein Panel mit einem Notizfeld wird oft geöffnet und geschlossen, und das Eingetippte soll bleiben. Was passt besser?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·