Startseite / Artikel / Umgang mit UI-Zuständen in der realen Welt mithilfe von Reacts bedingtem Rendering

Umgang mit UI-Zuständen in der realen Welt mithilfe von Reacts bedingtem Rendering

Erfahren Sie, wie Sie in React mithilfe praktischer bedingter Renderingsmuster Authentifizierungs-, Rollen-, Berechtigungs-, Lade-, Fehler- und leeren-Zustands-UIs erstellen.

2186 Wörter

In der vorherigen Folge haben Sie die Grundlagen der bedingten Darstellung behandelt – indem Sie einfache Bedingungen verwenden, um React mitzuteilen, ob ein bestimmter Komponente angezeigt werden soll oder gar nichts.

In der Praxis bleiben Anwendungen jedoch selten so einfach:

isLoggedIn ? <Dashboard /> : <Login />

Betrachten Sie ein echtes Dashboard. Ein Benutzer kann sich in einem der folgenden Zustände befinden:

  • Ausgeloggt
  • Im Anmeldeprozess
  • Eingeloggt
  • Admin
  • Normaler Benutzer
  • Fehlende erforderliche Berechtigung
  • Wartet auf eingehende Daten
  • Hat mit einem API-Fehler zu kämpfen
  • Betrachtet eine leere Ergebnismenge

Jeder dieser Szenarien erfordert eine eigene Benutzeroberfläche. Genau hier wird die bedingte Darstellung von einem kleinen Trick zu einem echten Werkzeug zur Strukturierung Ihrer Anwendung.

Lassen Sie uns anschauen, wie sich das in produktionstauglichem React-Code darstellt.

1. Authentifizierungsbasiertes Rendering

Ein klassischer Anwendungsfall ist die Authentifizierung. Stellen Sie sich eine App mit zwei möglichen Bildschirmen vor:

Not Logged In
      ↓
Login Page

Logged In
      ↓
Dashboard

React kann ohne große Anstrengung zwischen ihnen wählen:

function App() {
  const isLoggedIn = true;
return (
    <>
      {isLoggedIn ? <Dashboard /> : <Login />}
    </>
  );
}

Reale Apps benötigen jedoch in der Regel einen dritten Zustand: loading, da es eine Weile dauern kann, bis bestätigt ist, ob das Token des Benutzers gültig ist. Der Ablauf sieht dann so aus:

Checking Authentication
        ↓
      Loading
        ↓
  Authenticated?
   ↙          ↘
 YES          NO
 ↓             ↓
Dashboard     Login

In Code könnte das wie folgt dargestellt werden:

function App() {
  const isLoading = false;
  const isLoggedIn = true;
if (isLoading) {
    return <LoadingSpinner />;
  }
  return isLoggedIn
    ? <Dashboard />
    : <Login />;
}

Dieses genaue Muster findet sich in unzähligen produktiven React-Apps wieder.

2. rollenbasiertes Rendering

Die Authentifizierung zeigt an, wer eingeloggt ist. Die Autorisierung gibt an, was diese Person tun darf.

Nehmen wir ein Tools zur Mitarbeiterverwaltung als Beispiel. Ein Admin kann möglicherweise Folgendes sehen:

View Employees
Add Employee
Edit Employee
Delete Employee

Während ein Standardbenutzer nur Folgendes sieht:

View Employees

Man kann Aktionen abhängig von der Rolle des Benutzers bedingungsweise anzeigen:

function EmployeeCard({ userRole }) {
  return (
    <div>
      <h2>Employee Details</h2>
      <button>View</button>
      {userRole === "admin" && (
        <>
          <button>Edit</button>
          <button>Delete</button>
        </>
      )}
    </div>
  );
}

Durch diese Einstellung erscheinen Kontrollen, die nur für Admins bestimmt sind, ausschließlich für Admins.

Wichtiger Sicherheitshinweis

Bedingte Darstellung bestimmt, was in der UI angezeigt wird, doch das Verbergen eines Elements ist kein Ersatz für echte Sicherheit. Zum Beispiel:

{isAdmin && <DeleteButton />}

So kann verhindert werden, dass ein gewöhnlicher Benutzer die Schaltfläche sieht, doch der Server muss weiterhin unabhängig überprüfen, ob die Anfrage tatsächlich von jemandem stammt, der berechtigt ist, Daten zu löschen. Betrachten Sie es folgendermaßen:

Frontend
↓
Controls what users SEE

Backend
↓
Controls what users CAN DO

Behandeln Sie bedingte Darstellung auf Client-Seite niemals als Ihr Autorisierungsmechanismus.

3. UI basierend auf Berechtigungen

Größere Anwendungen benötigen oft mehr Feinabstimmung als einfache Rollen wie:

Admin
User

Anstelle dessen können Sie spezifische Berechtigungen definieren, wie zum Beispiel:

CAN_VIEW_USERS
CAN_EDIT_USERS
CAN_DELETE_USERS
CAN_EXPORT_REPORT

Die Komponenten können anschließend jede Aktion je nach verfügbaren Berechtigungen einzeln darstellen:

function UserActions({ permissions }) {
  return (
    <>
      {permissions.includes("CAN_EDIT_USERS") && (
        <button>Edit</button>
      )}
      {permissions.includes("CAN_DELETE_USERS") && (
        <button>Delete</button>
      )}
    </>
  );
}

Dieser Ansatz bietet eine viel präzisere Kontrolle darüber, was jeder Benutzer tun kann.

4. Ladezustände

Nehmen wir an, ein Dashboard sendet einen API-Aufruf, der zwei Sekunden zur Beantwortung benötigt. Was sollte in dieser Zeit auf dem Bildschirm angezeigt werden? Auf keinen Fall eine leere Seite – stattdessen sollte ein Ladeindikator erscheinen:

if (loading) {
  return <p>Loading products...</p>;
}

Der Gesamtfluss sieht wie folgt aus:

API Request
    ↓
Loading = true
    ↓
Show Loader
    ↓
API Response
    ↓
Loading = false
    ↓
Show Content

Ladeindikatoren sorgen dafür, dass eine App auch bei langsamer Netzverbindung reaktiv wirkt.

5. Skelett-Lader

Anstelle einer einfachen Textnachricht wie:

Loading...

zeigen viele moderne Benutzeroberflächen einen Platzhalter in der Form des Inhalts an, der gerade geladen wird – einen Skelett-Lader. Zum Beispiel:

┌──────────────────────┐
│ █████████████        │
│ ███████              │
│ █████████████████    │
└──────────────────────┘

Sobald die echten Daten zurückkommen, ersetzen sie den Platzhalter:

┌──────────────────────┐
│ MacBook Air          │
│ ₹99,999              │
│ ⭐⭐⭐⭐⭐             │
└──────────────────────┘

Die zugrunde liegende React-Logik bleibt genauso einfach:

return loading
  ? <ProductSkeleton />
  : <ProductCard />;

Der einzige wirkliche Unterschied hier ist die Verbesserung der Benutzererfahrung.

6. Fehlerzustände

Nicht immer verlaufen API-Aufrufe reibungslos.

Die Verbindung kann abbrechen.

Der Server kann abstürzen.

Eine Anfrage kann ablaufen.

Anstatt zu ermöglichen, dass Ihre App abstürzt oder einfriert, sollten Sie stattdessen einen Fehlerzustand anzeigen.

if (error) {
  return (
    <div>
      <h2>Something went wrong.</h2>
      <button>Try Again</button>
    </div>
  );
}

Das sanfte Handhaben von Fehlern ist ein Merkmal einer hochwertigen Benutzeroberfläche.

7. Laden + Fehler + Erfolg

In der Praxis treten diese drei Zustände fast immer gemeinsam auf.

function ProductList({
  loading,
  error,
  products
}) {
      if (loading) {
    return <p>Loading...</p>;
      }
  if (error) {
    return <p>Something went wrong.</p>;
      }
  return <Products products={products} />;
     }

Man kann den Ablauf so vorstellen:

Request
   │
   ├── Loading → Loader
   │
   ├── Failed → Error
   │
   └── Success → Data

Sobald Sie später in dieser Serie auf API-Aufrufe und useEffect stoßen, werden Sie dieses genaue Muster immer wieder beobachten.

8. Leere Zustände

Nur weil eine Anfrage erfolgreich ist, bedeutet das noch nicht, dass tatsächlich Daten angezeigt werden können.

Nehmen wir an, ein Benutzer sucht nach etwas wie:

"React Quantum Pizza Developer"

Der API-Aufruf selbst wird ohne Fehler abgeschlossen.

Doch das Ergebnis könnte so aussehen:

products.length === 0

Anstatt den Bildschirm leer zu lassen, sollten dem Benutzer sinnvolle Inhalte angezeigt werden.

if (products.length === 0) {
  return (
    <div>
      <h2>No Products Found</h2>
      <p>Try changing your search.</p>
    </div>
  );
}

Leere Zustände sind für ein ansprechendes Benutzererlebnis von großer Bedeutung.

Laden vs. Leer vs. Fehler

Neue React-Entwickler verwechseln diese drei Zustände oft, doch sie bezeichnen völlig unterschiedliche Situationen.

LOADING
Data hasn't arrived yet.

EMPTY
Data arrived, but nothing exists.

ERROR
Something failed.

Eine solide Anwendung berücksichtigt alle drei Zustände getrennt voneinander.

9. Mehrere Bedingungen

Mannchmal hängt das, was Sie ausgeben, von mehr als einer zusammengefassten Bedingung ab.

Betrachten Sie diesen Ablauf:

Is User Logged In?
        ↓
Is Subscription Active?
        ↓
Is User Admin?
        ↓
Show Admin Dashboard

Es ist verlockend, all das in einen einzigen riesigen verschachtelten ternären Ausdruck zu packen:

condition1
  ? condition2
    ? condition3
      ? <A />
      : <B />
    : <C />
  : <D />

Er kompiliert einwandfrei.

Aber er ist eine Qual zum Lesen.

Ein besseres Vorgehen besteht darin, jede Bedingung klar getrennt zu behandeln.

if (!isLoggedIn) {
  return <Login />;
}

if (!hasSubscription) {
  return <UpgradePlan />;
}

if (isAdmin) {
  return <AdminDashboard />;
}

return <UserDashboard />;

Diese Version ist weitaus leichter nachvollziehbar.

10. Guard-Klauzen

Das Muster, das Sie gerade gesehen haben, hat einen Namen: Guard-Klauzen, auch bekannt als frühzeitige Rückgänge.

Anstatt Bedingungen in immer weitere Schichten zu versenken:

if
 └── if
      └── if
           └── UI

erledigen Sie die Randfälle bereits im Voraus und kehren Sie früh zurück.

if (loading) return <Loader />;
if (error) return <ErrorPage />;
if (!user) return <Login />;
return <Dashboard />;

Das Ergebnis ist übersichtlich.

Es ist lesbar.

Außerdem ist es viel einfacher zu debuggen.

Denken Sie wie ein React-Entwickler

Vor dem Schreiben einer Komponente hilft es, sich folgende Frage zu stellen:

„Welche möglichen Zustände kann dieses Bildschirmdisplay haben?“

Bei einer von einer API-Aufruf gesteuerten Seite könnte diese Liste so aussehen:

Loading
Error
Empty
Success

Beim Authentifizierungsfluss könnte sie folgendermaßen aussehen:

Logged Out
Checking Authentication
Logged In
Unauthorized

Durch die Vorplanung dieser Zustände vor dem Starten der Programmierung wird das entstehende Komponentenmodell viel leichter verständlich.

Häufige Anfängerfehler

Aufhäufen von verschachtelten Ternären

Geben Sie die Lesbarkeit nicht auf, nur um ein paar Zeilen Code zu sparen.

Auslassen des leeren Zustands

Eine API, die ein leeres Array zurückgibt, ist nicht dasselbe wie ein Fehler – behandeln Sie es als eigenen Fall.

Verschmelzen von UI-Versteckung mit echter Sicherheit

Das Verstecken von Inhalten wie:

<DeleteButton />

hilft nicht dabei, zu verhindern, dass jemand direkt auf Ihren API-Endpunkt zugreift.

Echte Autorisierung muss im Backend umgesetzt werden.

Zulassen, dass && das Falsche darstellt

Achten Sie auf Code wie diesen:

{items.length && <ProductList />}

Falls items.length zufällig 0 ist, kann React am Ende Folgendes darstellen:

0

gerade dort auf der Seite.

Die sichere Variante ist:

{items.length > 0 && <ProductList />}

Nun wird die Bedingung zu einem echten Booleschen Wert ausgewertet.

Best Practices

Bei der bedingten Darstellung sollte die Lesbarkeit immer an erster Stelle stehen.

Ziehen Sie Vorzug vor Mustern wie:

if (loading) return <Loader />;

anstatt Bedingungen in tief verschachteltem JSX anzuhäufen.

Für Zustände, die Sie häufig darstellen, nehmen Sie sie in eigene wiederverwendbare Komponenten auf:

<Loader />
<ErrorMessage />
<EmptyState />

Je komplexer die Komponenten werden, desto mehr sollten Business-Logik und das tatsächlich Anzeigbare getrennt werden.

Und konzipieren Sie nicht nur für den „glücklichen“ Fall – planen Sie für jeden Zustand, in dem die Benutzeroberfläche realistischerweise sein könnte.

Kleines Projekt: Smartes Dashboard

Zum Üben versuchen Sie, ein Dashboard zu erstellen, das Fälle wie diese berücksichtigt:

User Not Logged In
        ↓
Login ScreenUser

    Logged In
        ↓
Loading Dashboard
        ↓
 ┌──────┴──────┐
Error          Success
 ↓                ↓
Error UI       Data Exists?
               ↙       ↘
             YES        NO
              ↓          ↓
          Dashboard   Empty State

Anschließend fügen Sie darauf basierend rollenbasiertes Verhalten hinzu:

Admin
↓
Edit + Delete

User
↓
View Only

Ein solches Projekt vereint gleichzeitig mehrere Konzepte:

  • Props
  • Zustand
  • Ereignisse
  • Bedingte Darstellung

Genau so beginnen die einzelnen Bestandteile von React miteinander zu interagieren, sobald man etwas Reales erstellt.

Interviewfragen

Was ist bedingte Darstellung?

Es handelt sich dabei um die Praxis, je nach aktuellen Zustand oder Bedingungen in der Anwendung eine andere Benutzeroberfläche anzuzeigen.

Was ist der Unterschied zwischen && und einem ternären Operator?

Verwenden Sie &&, wenn Sie möchten, dass etwas nur im wahren Fall angezeigt wird und ansonsten nichts erscheint. Verwenden Sie eine ternäre Logik, wenn Sie für die wahren und falschen Fälle unterschiedliche Ausgaben benötigen.

Was gilt als leere Zustand?

Es handelt sich um die Benutzeroberfläche, die angezeigt wird, wenn eine Anfrage erfolgreich ist, aber einfach keine Daten zum Anzeigen vorhanden sind.

Ist rollenbasiertes Rendering im Frontend ausreichend für die Sicherheit?

Nicht wirklich. Es ist lediglich eine Benutzerfreundlichkeit – tatsächliche Autorisierungsprüfungen müssen weiterhin im Backend durchgeführt werden.

Welchen Vorteil bieten frühe Rückgänge?

Sie verringern die Verkettung von Strukturen, wodurch bedingte Komponenten leichter lesbar und wartbar bleiben.

Kernpunkte

Bis hierhin haben Sie den gesamten Umfang des bedingten Renderings in React abgedeckt.

Sie haben gesehen, wie reale Anwendungen mit angemeldeten und unangemeldeten Ansichten umgehen, Bildschirme nach Rolle einschränken, Funktionen durch fein abgestimmte Berechtigungen freischalten, Ladevorgänge mit Spinners anzeigen, stattdessen Skeleton-Platzhalter statt reinen Textes verwenden, Fehler bei fehlgeschlagenen Anfragen anzeigen, Benutzern mitteilen, wenn eine Ergebnismenge leer ist, mehrere Bedingungen in einen kohärenten Ablauf kombinieren, verschachtelte Überprüfungen durch Schutzklauseln vereinfachen sowie die allgemeinen Praktiken anwenden, die diese Logik in einer echten Codebasis wartbar halten.

Bedingte Darstellung ermöglicht es Ihrer Anwendung, die richtige Erfahrung für jede aktuelle Situation anzubieten.

Aber es gibt noch eine Herausforderung.

Nehmen wir an, eine API gibt zurück:

1,000 Products

Würden Sie wirklich ausdrücklich:

<Product />
<Product />
<Product />
...

eintausendmal separat schreiben?

Offensichtlich nicht.

React bietet eine viel bessere Methode, um damit umzugehen.

In Teil 9A lernen Sie, wie man Listen mithilfe von map() darstellt, und sehen Sie, wie eine einzige Komponente direkt aus Ihren Daten Hunderte oder Tausende von UI-Elementen erzeugen kann.

Sofort danach werden Sie eines der klassischsten Interviewfragen zu React angehen:

Warum erfordert React eine key?

Wir sehen uns in Teil 9A – Darstellung von Listen und Keys in React.

Zusätzliche Literatur

  • Ein mentales Modell für React: Reconciliation, State und Hooks — Erhalten Sie Einblicke in die Logik hinter den Kernkonzepten von React – Reconciliation, Komponenten, Props, State und Hooks – um Intuition zu entwickeln anstelle von API-Auswendiglernen.
  • React-Komponenten 101: Erstellung wiederverwendbarer, wartbarer UI-Elemente — Erfahren Sie, warum die Aufteilung der Benutzeroberfläche in kleine React-Komponenten die Wiederverwendbarkeit, Lesbarkeit und Teamzusammenarbeit verbessert, und erstellen Sie anschließend Ihre erste funktionale Komponente.
  • Offline-Unterstützung in Webanwendungen mit Service Workers aktivieren — Erfahren Sie, wie Sie Service Workers sowie die Cache API nutzen können, um eine Website sofort laden zu lassen und deren Funktionstüchtigkeit auch ohne Internetverbindung aufrechtzuerhalten.
  • Die wahre Komplexität von modernem JavaScript rührt von Tools und nicht von der Sprache selbst her — Dieser Artikel erläutert, wie grundlegende JavaScript-Funktionen wie async/await und optional chaining den Code vereinfachen, während übermäßige Tools und Abhängigkeiten unnötige Komplexität verursachen.
  • React-Entwicklung mit KI: Echte Stärken, echte Beschränkungen — Erklärt, wo KI-Code-Assistenten die Arbeit mit React tatsächlich beschleunigen, wo sie versagen, sowie einen praktischen Arbeitsablauf für deren Nutzung ohne Einbußen bei der Codequalität.