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.
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
- React 19.2 erklärt: Activity, useEffectEvent und statische Darstellung – Erfahren Sie, wie die neuen Activity-Komponente, der useEffectEvent-Hook sowie die teilweise statische Darstellung in React 19.2 versteckte Leistungsprobleme in modernen UIs beheben.