Behersk React Suspense med praktiske mønstre for effektiv datahentning, indlæsningstilstande og robust fejlhåndtering. Byg mere flydende og robuste brugeroplevelser.
React Suspense Mønstre: Datahentning og Fejlgrænser
React Suspense er en kraftfuld funktion, der giver dig mulighed for at "suspende" komponentgengivelse, mens du venter på asynkrone operationer, såsom datahentning, for at fuldføre. Sammen med Fejlgrænser giver det en robust mekanisme til håndtering af indlæsningstilstande og fejl, hvilket resulterer i en mere flydende og robust brugeroplevelse. Denne artikel udforsker forskellige mønstre til effektivt at udnytte Suspense og Fejlgrænser i dine React-applikationer.
Forståelse af React Suspense
I sin kerne er Suspense en mekanisme, der lader React vente på noget, før den gengiver en komponent. Dette "noget" er typisk en asynkron operation, som f.eks. at hente data fra en API. I stedet for at vise en blank skærm eller en potentielt vildledende mellemtilstand kan du vise en fallback-UI (f.eks. en indlæsningsspinner), mens dataene indlæses.
Den vigtigste fordel er forbedret oplevet ydelse og en mere behagelig brugeroplevelse. Brugerne præsenteres straks for visuel feedback, der indikerer, at der sker noget, i stedet for at spekulere på, om applikationen er frosset.
Nøglebegreber
- Suspense Komponent:
<Suspense>komponenten ombryder komponenter, der muligvis suspenderes. Den accepterer enfallbackprop, som specificerer den UI, der skal gengives, mens de omviklede komponenter er suspenderet. - Fallback UI: Dette er den UI, der vises, mens den asynkrone operation er i gang. Det kan være alt fra en simpel indlæsningsspinner til en mere detaljeret animation.
- Promise Integration: Suspense fungerer med Promises. Når en komponent forsøger at læse en værdi fra en Promise, der endnu ikke er blevet løst, suspenderer React komponenten og viser fallback UI'en.
- Datakilder: Suspense er afhængig af datakilder, der er Suspense-aware. Disse kilder eksponerer en API, der giver React mulighed for at detektere, hvornår data hentes.
Hentning af Data med Suspense
For at bruge Suspense til datahentning skal du bruge et Suspense-aware datahentningsbibliotek. Her er en almindelig tilgang ved hjælp af en brugerdefineret `fetchData`-funktion:
Eksempel: Simpel Datahentning
Opret først en hjælpefunktion til hentning af data. Denne funktion skal håndtere det 'suspenderende' aspekt. Vi vil ombryde vores fetch-kald i en brugerdefineret ressource for korrekt at håndtere promise-tilstanden.
// utils/api.js
const wrapPromise = (promise) => {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
}
return result;
},
};
};
const fetchData = (url) => {
const promise = fetch(url)
.then((res) => res.json())
.then((data) => data);
return wrapPromise(promise);
};
export default fetchData;
Lad os nu oprette en komponent, der bruger Suspense til at vise brugerdata:
// components/UserProfile.js
import React from 'react';
import fetchData from '../utils/api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
export default UserProfile;
Til sidst skal du ombryde UserProfile komponenten med <Suspense>:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
function App() {
return (
<Suspense fallback={<p>Indlæser brugerdata...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
I dette eksempel forsøger UserProfile komponenten at læse user dataene fra resource. Hvis dataene endnu ikke er tilgængelige (Promise er stadig i venteposition), suspenderer komponenten, og fallback UI'en ("Indlæser brugerdata...") vises. Når dataene er hentet, gengives komponenten igen med de faktiske brugeroplysninger.
Fordele ved denne tilgang
- Deklarativ datahentning: Komponenten udtrykker *hvilke* data den har brug for, ikke *hvordan* den skal hentes.
- Centraliseret håndtering af indlæsningstilstand: Suspense komponenten håndterer indlæsningstilstanden, hvilket forenkler komponentlogikken.
Fejlgrænser for Robusthed
Mens Suspense håndterer indlæsningstilstande elegant, håndterer den ikke i sig selv fejl, der kan opstå under datahentning eller komponentgengivelse. Det er her, Fejlgrænser kommer ind i billedet.
Fejlgrænser er React-komponenter, der fanger JavaScript-fejl overalt i deres underordnede komponenttræ, logger disse fejl og viser en fallback UI i stedet for at crashe hele applikationen. De er afgørende for at opbygge robuste brugergrænseflader, der elegant kan håndtere uventede fejl.
Oprettelse af en Fejlgrænse
For at oprette en Fejlgrænse skal du definere en klassekomponent, der implementerer static getDerivedStateFromError() og componentDidCatch() lifecycle-metoderne.
// components/ErrorBoundary.js
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Opdater tilstanden, så den næste gengivelse viser fallback UI'en.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, errorInfo) {
// Du kan også logge fejlen til en fejlrapporteringstjeneste
console.error("Caught error: ", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// Du kan gengive en hvilken som helst brugerdefineret fallback UI
return (
<div>
<h2>Noget gik galt.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
getDerivedStateFromError metoden kaldes, når der kastes en fejl i en efterkommerkomponent. Den opdaterer tilstanden for at indikere, at der er opstået en fejl.
componentDidCatch metoden kaldes, efter at der er blevet kastet en fejl. Den modtager fejlen og fejlinformationen, som du kan bruge til at logge fejlen til en fejlrapporteringstjeneste eller vise en mere informativ fejlmeddelelse.
Brug af Fejlgrænser med Suspense
For at kombinere Fejlgrænser med Suspense skal du blot ombryde <Suspense> komponenten med en <ErrorBoundary> komponent:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
import ErrorBoundary from './components/ErrorBoundary';
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Indlæser brugerdata...</p>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}
export default App;
Hvis der nu opstår en fejl under datahentning eller gengivelse af UserProfile komponenten, vil Fejlgrænsen fange fejlen og vise fallback UI'en, hvilket forhindrer hele applikationen i at crashe.
Avancerede Suspense Mønstre
Ud over grundlæggende datahentning og fejlhåndtering tilbyder Suspense flere avancerede mønstre til opbygning af mere sofistikerede brugergrænseflader.
Kodeopdeling med Suspense
Kodeopdeling er processen med at opdele din applikation i mindre bidder, der kan indlæses efter behov. Dette kan forbedre den indledende indlæsningstid for din applikation betydeligt.
React.lazy og Suspense gør kodeopdeling utrolig nemt. Du kan bruge React.lazy til dynamisk at importere komponenter og derefter ombryde dem med <Suspense> for at vise en fallback UI, mens komponenterne indlæses.
// components/MyComponent.js
import React from 'react';
const MyComponent = React.lazy(() => import('./AnotherComponent'));
function App() {
return (
<Suspense fallback={<p>Indlæser komponent...</p>}>
<MyComponent />
</Suspense>
);
}
export default App;
I dette eksempel indlæses MyComponent efter behov. Mens den indlæses, vises fallback UI'en ("Indlæser komponent..."). Når komponenten er indlæst, gengives den normalt.
Parallel Datahentning
Suspense giver dig mulighed for at hente flere datakilder parallelt og vise en enkelt fallback UI, mens alle dataene indlæses. Dette kan være nyttigt, når du har brug for at hente data fra flere API'er for at gengive en enkelt komponent.
import React, { Suspense } from 'react';
import fetchData from './api';
const userResource = fetchData('https://jsonplaceholder.typicode.com/users/1');
const postsResource = fetchData('https://jsonplaceholder.typicode.com/posts?userId=1');
function UserProfile() {
const user = userResource.read();
const posts = postsResource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<h3>Opslag:</h3>
<ul>
{posts.map(post => (<li key={post.id}>{post.title}</li>))}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback={<p>Indlæser brugerdata og opslag...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
I dette eksempel henter UserProfile komponenten både brugerdata og opslagsdata parallelt. <Suspense> komponenten viser en enkelt fallback UI, mens begge datakilder indlæses.
Transition API med useTransition
React 18 introducerede useTransition hook, som forbedrer Suspense ved at give en måde at administrere UI-opdateringer som overgange. Det betyder, at du kan markere visse tilstandsopdateringer som mindre presserende og forhindre dem i at blokere brugergrænsefladen. Dette er især nyttigt, når man beskæftiger sig med langsommere datahentning eller komplekse gengivelsesoperationer, hvilket forbedrer oplevet ydelse.
Her er hvordan du kan bruge useTransition:
import React, { useState, Suspense, useTransition } from 'react';
import fetchData from './api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
function App() {
const [isPending, startTransition] = useTransition();
const [showProfile, setShowProfile] = useState(false);
const handleClick = () => {
startTransition(() => {
setShowProfile(true);
});
};
return (
<div>
<button onClick={handleClick} disabled={isPending}>
Vis Brugerprofil
</button>
{isPending && <p>Indlæser...</p>}
<Suspense fallback={<p>Indlæser brugerdata...</p>}>
{showProfile && <UserProfile />}
</Suspense>
</div>
);
}
export default App;
I dette eksempel initierer et klik på knappen "Vis Brugerprofil" en overgang. startTransition markerer setShowProfile-opdateringen som en overgang, hvilket giver React mulighed for at prioritere andre UI-opdateringer. Værdien isPending fra useTransition angiver, om en overgang er i gang, hvilket giver dig mulighed for at give visuel feedback (f.eks. deaktivere knappen og vise en indlæsningsbesked).
Bedste Praksisser for Brug af Suspense og Fejlgrænser
- Ombryd Suspense omkring det mindst mulige område: Undgå at ombryde store dele af din applikation med
<Suspense>. I stedet skal du kun ombryde de komponenter, der faktisk har brug for at suspenderes. Dette vil minimere indvirkningen på resten af UI'en. - Brug meningsfulde fallback UI'er: Fallback UI'en skal give brugerne klar og informativ feedback om, hvad der sker. Undgå generiske indlæsningsspinnere; prøv i stedet at give mere kontekst (f.eks. "Indlæser brugerdata...").
- Placer Fejlgrænser strategisk: Tænk nøje over, hvor du skal placere Fejlgrænser. Placer dem højt nok i komponenttræet til at fange fejl, der kan påvirke flere komponenter, men lavt nok til at undgå at fange fejl, der er specifikke for en enkelt komponent.
- Log fejl: Brug
componentDidCatchmetoden til at logge fejl til en fejlrapporteringstjeneste. Dette vil hjælpe dig med at identificere og rette fejl i din applikation. - Giv brugervenlige fejlmeddelelser: Fallback UI'en, der vises af Fejlgrænser, skal give brugerne nyttige oplysninger om fejlen, og hvad de kan gøre ved den. Undgå teknisk jargon; brug i stedet et klart og præcist sprog.
- Test dine Fejlgrænser: Sørg for, at dine Fejlgrænser fungerer korrekt ved bevidst at kaste fejl i din applikation.
Internationale Overvejelser
Når du bruger Suspense og Fejlgrænser i internationale applikationer, skal du overveje følgende:
- Lokalisering: Sørg for, at fallback UI'erne og fejlmeddelelserne er korrekt lokaliseret for hvert sprog, der understøttes af din applikation. Brug internationaliseringsbiblioteker (i18n) som
react-intlelleri18nexttil at administrere oversættelser. - Højre-til-venstre (RTL) layouts: Hvis din applikation understøtter RTL-sprog (f.eks. arabisk, hebraisk), skal du sørge for, at fallback UI'erne og fejlmeddelelserne vises korrekt i RTL-layouts. Brug CSS logiske egenskaber (f.eks.
margin-inline-starti stedet formargin-left) til at understøtte både LTR- og RTL-layouts. - Tilgængelighed: Sørg for, at fallback UI'erne og fejlmeddelelserne er tilgængelige for brugere med handicap. Brug ARIA-attributter til at give semantisk information om indlæsningstilstanden og fejlmeddelelserne.
- Kulturel følsomhed: Vær opmærksom på kulturelle forskelle, når du designer fallback UI'er og fejlmeddelelser. Undgå at bruge billeder eller sprog, der kan være stødende eller upassende i visse kulturer. For eksempel kan en almindelig indlæsningsspinner opfattes negativt i nogle kulturer.
Eksempel: Lokaliseret Fejlmeddelelse
Ved hjælp af react-intl kan du oprette lokaliserede fejlmeddelelser:
// components/ErrorBoundary.js
import React from 'react';
import { FormattedMessage } from 'react-intl';
class ErrorBoundary extends React.Component {
// ... (samme som før)
render() {
if (this.state.hasError) {
return (
<div>
<h2><FormattedMessage id="error.title" defaultMessage="Something went wrong." /></h2>
<p><FormattedMessage id="error.message" defaultMessage="Please try again later." /></p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Definer derefter oversættelserne i dine lokalefiler:
// locales/en.json
{
"error.title": "Something went wrong.",
"error.message": "Please try again later."
}
// locales/fr.json
{
"error.title": "Quelque chose s'est mal passé.",
"error.message": "Veuillez réessayer plus tard."
}
Konklusion
React Suspense og Fejlgrænser er vigtige værktøjer til opbygning af moderne, robuste og brugervenlige brugergrænseflader. Ved at forstå og anvende de mønstre, der er beskrevet i denne artikel, kan du forbedre den opfattede ydeevne og den generelle kvalitet af dine React-applikationer betydeligt. Husk at overveje internationalisering og tilgængelighed for at sikre, at dine applikationer kan bruges af et globalt publikum.
Asynkron datahentning og korrekt fejlhåndtering er kritiske aspekter af enhver webapplikation. Suspense, kombineret med Fejlgrænser, tilbyder en deklarativ og effektiv måde at håndtere disse kompleksiteter i React, hvilket resulterer i en mere jævn og pålidelig brugeroplevelse for brugere over hele verden.