Verbesserung der Ladeerfahrung von React mit Suspense
Hast du beim Erstellen von Anwendungen mit React schon mal die folgende Situation erlebt? Eine Seite muss eine große Menge an Daten laden oder eine Aktion dauert ziemlich lange. Die Benutzeroberfläche friert für einige Sekunden ein, und dann – bam! – wird alles auf einmal aktualisiert. Diese Art von Verzögerung ist sehr störend und wirkt nicht ansprechend. Gibt es also eine Möglichkeit, den Rest der Seite zu rendern, während wir auf zeitaufwändige Vorgänge warten? Und die verzögerten Inhalte erst anzuzeigen, wenn sie fertig sind?
Ich möchte hier ein klassisches (und niedliches) Beispiel nennen: das Ausbrüten von Pokémon-Eiern.
Zunächst müssen wir die Liste der Pokémon abrufen, die kurz vor dem Schlüpfen stehen:
const list = await fetchPokemonList();
Das await
blockiert die weitere Ausführung, bis die Daten zurückgegeben werden. Das bedeutet, dass die gesamte Komponente wartet und nichts rendert. Der Benutzer sieht nur einen leeren Bildschirm. Nach 2 Sekunden erscheint die Liste plötzlich. Einige ungeduldige User haben möglicherweise bereits angenommen, dass die Seite defekt ist, und begonnen, auf die Schaltfläche „Aktualisieren“ zu klicken.
Hier kommt <Suspense>
ins Spiel.
Schritt 1: Basic Suspense – Liste ausbrüten
Wir umschließen die Listenkomponente mit einer Suspense
- Begrenzung und stellen während des Wartens einen Fallback-Spinner bereit:
export async function Pokemon() {
const list = fetchPokemonList();
return (
<div>
<p>Try your luck! Which Pokémon can you hatch from the Eggs?</p>
<Suspense fallback={<LoadingListSpinner />}>
<List promise={list} />
</Suspense>
</div>
);
}
Wir fügen await
in die List-Komponente ein, die auf die Daten wartet und dann das Pokémon rendert:
async function List(props: {
promise: Promise<Pokemon[]>;
}) {
const list = await props.promise;
return (
<div>
{list.map(async (pokemon) => (<PokemonCard pokemon={pokemon}/>))}
</div>
);
}
Während die Pokémon-Liste noch geladen wird, wird sie ausgesetzt und React rendert stattdessen zuerst die Fallback-Komponente. In der Zwischenzeit bleiben andere Teile der Seite sichtbar.
)
Viel besser, oder? Jetzt können die User erkennen: „Ah, sie schlüpfen! Ich werde gleich mein Pokémon sehen!“
Schritt 2: Suspense per Item – Lass die schnellen zuerst schlüpfen
Aber es gibt noch ein weiteres Problem.
Was passiert, wenn ein einzelnes Pokémon langsam lädt? Es wird zum Engpass und blockiert damit die gesamte Liste. Kein Pokémon wird angezeigt, bis das langsamste verfügbar ist. Autsch.
Was können wir tun? Ja, <Suspense>
kann verschachtelt werden! Verbessern wir das, indem wir jedes Pokémon-Item mit seinem eigenen Suspense umschließen. Die schnelleren Pokémon können zuerst erscheinen, während die langsameren weiterhin einen Spinner anzeigen:
async function List(props: {
promise: Promise<Pokemon[]>;
}) {
const list = await props.promise;
return (
<div>
{list.map((pokemon) => (
<div>
<p>{pokemon.name}</p>
<Suspense fallback={<Spinner />}>
<CardContent promise={fetchPokemonImage(pokemon)} />
</Suspense>
</div>
))}
</div>
);
}
async function CardContent(props: { promise: Promise<Pokemon> }) {
const pokemon = await props.promise;
return ( <Image src={pokemon.image} /> );
}
)
Bonus: Mit useTransition werden Aktualisierungen noch flüssiger
Suspense
funktioniert hervorragend. Nach dem Klick beginnt die Seite zu laden, doch die Benutzeroberfläche ändert sich nicht sofort, sodass der Nutzer ein Gefühl der Verzögerung wahrnimmt.
)
In solchen Fällen können wir es mit useTransition
kombinieren, um die visuelle Geschmeidigkeit zu verbessern.
const [isPending, startTransition] = useTransition();
const onClick = () => {
startTransition(() => {
navigateToNewPage();
});
};
Dieser Hook ermöglicht es, die alte Benutzeroberfläche sichtbar zu halten, während die neue Benutzeroberfläche im Hintergrund gerendert wird. Während dieses Prozesses stellt er einen Pending-Status bereit, den wir nutzen können, um besseres Feedback zu geben und ein flüssigeres Nutzererlebnis zu schaffen. Dadurch erkennt der Nutzer, dass die Oberfläche nach dem Klick reagiert.
)
Fazit
React Suspense ist ein leistungsstarkes Tool, das die wahrnehmbare Leistung erheblich verbessern kann:
- Es zeigt Fallbacks an, während langsame Teile der Seite geladen werden.
- Es rendert schnellere Teile zuerst mit verschachteltem Suspense.
- Nutzererlebnis mit useTransition verbessern.
Einmal ausprobiert, wird sich die App viel flüssiger und reaktionsschneller anfühlen. Also los – lass noch heute ein paar Suspense-aktivierte Pokémon schlüpfen.
)
)
)
)
)
)
)
)
)
)
)
)