En omfattende guide for å forstå og implementere JavaScript Error Boundaries i React for robust feilhåndtering og elegant UI-degradering.
JavaScript Error Boundary: En implementeringsguide for feilhåndtering i React
Innenfor React-utvikling kan uventede feil føre til frustrerende brukeropplevelser og ustabilitet i applikasjonen. En veldefinert strategi for feilhåndtering er avgjørende for å bygge robuste og pålitelige applikasjoner. Reacts Error Boundaries gir en kraftig mekanisme for å håndtere feil som oppstår i komponenttreet ditt på en elegant måte, noe som forhindrer hele applikasjonen i å krasje og lar deg vise et reserve-UI (fallback UI).
Hva er en Error Boundary?
En Error Boundary er en React-komponent som fanger opp JavaScript-feil hvor som helst i sitt underordnede komponenttre, logger disse feilene og viser et reserve-UI i stedet for komponenttreet som krasjet. Error Boundaries fanger opp feil under rendering, i livssyklusmetoder og i konstruktører for hele treet under dem.
Tenk på en Error Boundary som en try...catch
-blokk for React-komponenter. Akkurat som en try...catch
-blokk lar deg håndtere unntak i synkron JavaScript-kode, lar en Error Boundary deg håndtere feil som oppstår under renderingen av dine React-komponenter.
Viktig merknad: Error Boundaries fanger ikke opp feil for:
- Hendelseshåndterere (event handlers) (lær mer i de følgende avsnittene)
- Asynkron kode (f.eks.
setTimeout
ellerrequestAnimationFrame
-callbacks) - Server-side rendering (SSR)
- Feil som kastes i selve Error Boundary-komponenten (i stedet for i dens barn)
Hvorfor bruke Error Boundaries?
Å bruke Error Boundaries gir flere betydelige fordeler:
- Forbedret brukeropplevelse: I stedet for å vise en blank hvit skjerm eller en kryptisk feilmelding, kan du vise et brukervennlig reserve-UI som informerer brukeren om at noe gikk galt og potensielt tilbyr en måte å gjenopprette på (f.eks. ved å laste siden på nytt eller navigere til en annen seksjon).
- Applikasjonsstabilitet: Error Boundaries forhindrer at feil i én del av applikasjonen krasjer hele applikasjonen. Dette er spesielt viktig for komplekse applikasjoner med mange sammenkoblede komponenter.
- Sentralisert feilhåndtering: Error Boundaries gir et sentralisert sted for å logge feil og spore opp årsaken til problemer. Dette forenkler feilsøking og vedlikehold.
- Elegant degradering: Du kan strategisk plassere Error Boundaries rundt forskjellige deler av applikasjonen for å sikre at selv om noen komponenter feiler, forblir resten av applikasjonen funksjonell. Dette muliggjør elegant degradering når feil oppstår.
Implementering av Error Boundaries i React
For å lage en Error Boundary, må du definere en klassekomponent som implementerer én (eller begge) av følgende livssyklusmetoder:
static getDerivedStateFromError(error)
: Denne livssyklusmetoden kalles etter at en feil er kastet av en underordnet komponent. Den mottar feilen som ble kastet som et argument og skal returnere en verdi for å oppdatere komponentens tilstand for å indikere at en feil har oppstått (f.eks. ved å sette ethasError
-flagg tiltrue
).componentDidCatch(error, info)
: Denne livssyklusmetoden kalles etter at en feil er kastet av en underordnet komponent. Den mottar feilen som ble kastet som et argument, sammen med etinfo
-objekt som inneholder informasjon om hvilken komponent som kastet feilen. Du kan bruke denne metoden til å logge feilen til en tjeneste som Sentry eller Bugsnag.
Her er et grunnleggende eksempel på en Error Boundary-komponent:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Oppdater state slik at neste render vil vise reserve-UI-et.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, info) {
// Eksempel "componentStack":
// in ComponentThatThrows (created by App)
// in MyErrorBoundary (created by App)
// in div (created by App)
// in App
console.error("Fanget en feil:", error, info);
this.setState({
errorInfo: info.componentStack
});
// Du kan også logge feilen til en feilrapporteringstjeneste
//logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Du kan rendere hvilket som helst tilpasset reserve-UI
return (
<div>
<h2>Noe gikk galt.</h2>
<p>Feil: {this.state.error ? this.state.error.message : "En ukjent feil oppstod."}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.errorInfo && this.state.errorInfo}
</details>
</div>
);
}
return this.props.children;
}
}
For å bruke Error Boundary, pakker du enkelt og greit inn komponenttreet du vil beskytte:
<ErrorBoundary>
<MyComponentThatMightThrow/>
</ErrorBoundary>
Praktiske eksempler på bruk av Error Boundary
La oss utforske noen praktiske scenarioer der Error Boundaries kan være spesielt nyttige:
1. Håndtering av API-feil
Når du henter data fra et API, kan feil oppstå på grunn av nettverksproblemer, serverproblemer eller ugyldige data. Du kan pakke inn komponenten som henter og viser dataene med en Error Boundary for å håndtere disse feilene elegant.
function UserProfile() {
const [user, setUser] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
async function fetchData() {
try {
const response = await fetch('/api/user');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
// Feilen vil bli fanget av ErrorBoundary
throw error;
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) {
return <p>Laster brukerprofil...</p>;
}
if (!user) {
return <p>Ingen brukerdata tilgjengelig.</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
function App() {
return (
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
);
}
I dette eksemplet, hvis API-kallet mislykkes eller returnerer en feil, vil Error Boundary fange opp feilen og vise et reserve-UI (definert i Error Boundary-komponentens render
-metode). Dette forhindrer hele applikasjonen i å krasje og gir brukeren en mer informativ melding. Du kan utvide reserve-UI-et til å gi en mulighet for å prøve forespørselen på nytt.
2. Håndtering av feil fra tredjepartsbiblioteker
Når du bruker tredjepartsbiblioteker, er det mulig at de kan kaste uventede feil. Å pakke inn komponenter som bruker disse bibliotekene med Error Boundaries kan hjelpe deg med å håndtere disse feilene elegant.
Tenk deg et hypotetisk diagrambibliotek som av og til kaster feil på grunn av datainkonsistens eller andre problemer. Du kan pakke inn diagramkomponenten slik:
function MyChartComponent() {
try {
// Render diagrammet ved hjelp av tredjepartsbiblioteket
return <Chart data={data} />;
} catch (error) {
// Denne catch-blokken vil ikke være effektiv for feil i React-komponentens livssyklus
// Den er primært for synkrone feil innenfor denne spesifikke funksjonen.
console.error("Feil ved rendering av diagram:", error);
// Vurder å kaste feilen slik at den fanges av ErrorBoundary
throw error; // Kaster feilen videre
}
}
function App() {
return (
<ErrorBoundary>
<MyChartComponent />
</ErrorBoundary>
);
}
Hvis Chart
-komponenten kaster en feil, vil Error Boundary fange den opp og vise et reserve-UI. Merk at try/catch-blokken inne i MyChartComponent bare vil fange feil innenfor den synkrone funksjonen, ikke i komponentens livssyklus. Derfor er ErrorBoundary helt avgjørende her.
3. Håndtering av renderingsfeil
Feil kan oppstå under renderingsprosessen på grunn av ugyldige data, feil prop-typer eller andre problemer. Error Boundaries kan fange opp disse feilene og forhindre at applikasjonen krasjer.
function DisplayName({ name }) {
if (typeof name !== 'string') {
throw new Error('Navn må være en streng');
}
return <h2>Hei, {name}!</h2>;
}
function App() {
return (
<ErrorBoundary>
<DisplayName name={123} /> <!-- Feil prop-type -->
</ErrorBoundary>
);
}
I dette eksemplet forventer DisplayName
-komponenten at name
-propen er en streng. Hvis et tall sendes inn i stedet, vil en feil bli kastet, og Error Boundary vil fange den opp og vise et reserve-UI.
Error Boundaries og hendelseshåndterere
Som nevnt tidligere, fanger Error Boundaries ikke opp feil som oppstår i hendelseshåndterere (event handlers). Dette er fordi hendelseshåndterere vanligvis er asynkrone, og Error Boundaries fanger kun opp feil som skjer under rendering, i livssyklusmetoder og i konstruktører.
For å håndtere feil i hendelseshåndterere, må du bruke en tradisjonell try...catch
-blokk inne i hendelseshåndtererfunksjonen.
function MyComponent() {
const handleClick = () => {
try {
// Noe kode som kan kaste en feil
throw new Error('En feil oppstod i hendelseshåndtereren');
} catch (error) {
console.error('Fanget en feil i hendelseshåndtereren:', error);
// Håndter feilen (f.eks. vis en feilmelding til brukeren)
}
};
return <button onClick={handleClick}>Klikk her</button>;
}
Global feilhåndtering
Selv om Error Boundaries er utmerkede for å håndtere feil innenfor React-komponenttreet, dekker de ikke alle mulige feilscenarioer. For eksempel fanger de ikke opp feil som oppstår utenfor React-komponenter, som feil i globale hendelseslyttere eller feil i kode som kjører før React er initialisert.
For å håndtere denne typen feil, kan du bruke window.onerror
-hendelseshåndtereren.
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global feilhåndterer:', message, source, lineno, colno, error);
// Logg feilen til en tjeneste som Sentry eller Bugsnag
// Vis en global feilmelding til brukeren (valgfritt)
return true; // Forhindrer standard feilhåndteringsoppførsel
};
window.onerror
-hendelseshåndtereren kalles hver gang en ufanget JavaScript-feil oppstår. Du kan bruke den til å logge feilen, vise en global feilmelding til brukeren eller utføre andre handlinger for å håndtere feilen.
Viktig: Å returnere true
fra window.onerror
-hendelseshåndtereren forhindrer nettleseren i å vise standardfeilmeldingen. Vær imidlertid oppmerksom på brukeropplevelsen; hvis du undertrykker standardmeldingen, må du sørge for å gi et klart og informativt alternativ.
Beste praksis for bruk av Error Boundaries
Her er noen beste praksis-tips du bør huske på når du bruker Error Boundaries:
- Plasser Error Boundaries strategisk: Pakk inn forskjellige deler av applikasjonen din med Error Boundaries for å isolere feil og forhindre at de sprer seg. Vurder å pakke inn hele ruter eller store deler av brukergrensesnittet ditt.
- Gi et informativt reserve-UI: Reserve-UI-et bør informere brukeren om at en feil har oppstått og potensielt tilby en måte å gjenopprette på. Unngå å vise generiske feilmeldinger som "Noe gikk galt."
- Logg feil: Bruk
componentDidCatch
-livssyklusmetoden for å logge feil til en tjeneste som Sentry eller Bugsnag. Dette vil hjelpe deg med å spore opp årsaken til problemer og forbedre stabiliteten i applikasjonen din. - Ikke bruk Error Boundaries for forventede feil: Error Boundaries er designet for å håndtere uventede feil. For forventede feil (f.eks. valideringsfeil, API-feil), bruk mer spesifikke feilhåndteringsmekanismer, som
try...catch
-blokker eller tilpassede feilhåndteringskomponenter. - Vurder flere nivåer av Error Boundaries: Du kan nøste Error Boundaries for å gi forskjellige nivåer av feilhåndtering. For eksempel kan du ha en global Error Boundary som fanger opp alle uhåndterte feil og viser en generisk feilmelding, og mer spesifikke Error Boundaries som fanger feil i bestemte komponenter og viser mer detaljerte feilmeldinger.
- Ikke glem server-side rendering: Hvis du bruker server-side rendering, må du også håndtere feil på serveren. Error Boundaries fungerer på serveren, men du må kanskje bruke ytterligere feilhåndteringsmekanismer for å fange feil som oppstår under den første renderingen.
Avanserte teknikker for Error Boundary
1. Bruk av en Render Prop
I stedet for å rendere et statisk reserve-UI, kan du bruke en render prop for å gi mer fleksibilitet i hvordan feil håndteres. En render prop er en funksjonsprop som en komponent bruker for å rendere noe.
class ErrorBoundary extends React.Component {
// ... (samme som før)
render() {
if (this.state.hasError) {
// Bruk render-propen til å rendere reserve-UI-et
return this.props.fallbackRender(this.state.error, this.state.errorInfo);
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary fallbackRender={(error, errorInfo) => (
<div>
<h2>Noe gikk galt!</h2>
<p>Feil: {error.message}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{errorInfo.componentStack}
</details>
</div>
)}>
<MyComponentThatMightThrow/>
</ErrorBoundary>
);
}
Dette lar deg tilpasse reserve-UI-et for hver enkelt Error Boundary. fallbackRender
-propen mottar feilen og feilinformasjonen som argumenter, noe som lar deg vise mer spesifikke feilmeldinger eller utføre andre handlinger basert på feilen.
2. Error Boundary som en Higher-Order Component (HOC)
Du kan lage en higher-order component (HOC) som pakker inn en annen komponent med en Error Boundary. Dette kan være nyttig for å bruke Error Boundaries på flere komponenter uten å måtte gjenta den samme koden.
function withErrorBoundary(WrappedComponent) {
return class WithErrorBoundary extends React.Component {
render() {
return (
<ErrorBoundary>
<WrappedComponent {...this.props} />
</ErrorBoundary>
);
}
};
}
// Bruk:
const MyComponentWithErrorHandling = withErrorBoundary(MyComponentThatMightThrow);
Funksjonen withErrorBoundary
tar en komponent som et argument og returnerer en ny komponent som pakker inn den opprinnelige komponenten med en Error Boundary. Dette lar deg enkelt legge til feilhåndtering i hvilken som helst komponent i applikasjonen din.
Testing av Error Boundaries
Det er viktig å teste dine Error Boundaries for å sikre at de fungerer som de skal. Du kan bruke testbiblioteker som Jest og React Testing Library for å teste dine Error Boundaries.
Her er et eksempel på hvordan du kan teste en Error Boundary ved hjelp av React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('Denne komponenten kaster en feil');
}
test('renderer reserve-UI når en feil kastes', () => {
render(
<ErrorBoundary>
<ComponentThatThrows />
</ErrorBoundary>
);
expect(screen.getByText('Noe gikk galt.')).toBeInTheDocument();
});
Denne testen renderer ComponentThatThrows
-komponenten, som kaster en feil. Testen verifiserer deretter at reserve-UI-et som renderes av Error Boundary blir vist.
Error Boundaries og Server Components (React 18+)
Med introduksjonen av Server Components i React 18 og senere, fortsetter Error Boundaries å spille en viktig rolle i feilhåndtering. Server Components kjøres på serveren og sender kun det ferdig-renderede resultatet til klienten. Selv om kjerneprinsippene forblir de samme, er det noen nyanser å vurdere:
- Server-side feillogging: Sørg for at du logger feil som oppstår i Server Components på serveren. Dette kan innebære å bruke et server-side loggingsrammeverk eller sende feil til en feilsporingstjeneste.
- Klient-side reserve-UI: Selv om Server Components renderes på serveren, må du fortsatt tilby et reserve-UI på klientsiden i tilfelle feil. Dette sikrer at brukeren har en konsistent opplevelse, selv om serveren ikke klarer å rendere komponenten.
- Streaming SSR: Når du bruker streaming Server-Side Rendering (SSR), kan feil oppstå under streaming-prosessen. Error Boundaries kan hjelpe deg med å håndtere disse feilene elegant ved å rendere et reserve-UI for den berørte strømmen.
Feilhåndtering i Server Components er et område i utvikling, så det er viktig å holde seg oppdatert på de nyeste beste praksisene og anbefalingene.
Vanlige fallgruver å unngå
- Overdreven bruk av Error Boundaries: Ikke bruk Error Boundaries som en erstatning for skikkelig feilhåndtering i komponentene dine. Sikt alltid på å skrive robust og pålitelig kode som håndterer feil elegant.
- Ignorering av feil: Sørg for at du logger feil som fanges opp av Error Boundaries, slik at du kan spore opp årsaken til problemene. Ikke bare vis et reserve-UI og ignorer feilen.
- Bruk av Error Boundaries for valideringsfeil: Error Boundaries er ikke det rette verktøyet for å håndtere valideringsfeil. Bruk mer spesifikke valideringsteknikker i stedet.
- Ikke å teste Error Boundaries: Test dine Error Boundaries for å sikre at de fungerer som de skal.
Konklusjon
Error Boundaries er et kraftig verktøy for å bygge robuste og pålitelige React-applikasjoner. Ved å forstå hvordan du implementerer og bruker Error Boundaries effektivt, kan du forbedre brukeropplevelsen, forhindre applikasjonskrasj og forenkle feilsøking. Husk å plassere Error Boundaries strategisk, gi et informativt reserve-UI, logge feil og teste dine Error Boundaries grundig.
Ved å følge retningslinjene og beste praksis som er beskrevet i denne guiden, kan du sikre at dine React-applikasjoner er motstandsdyktige mot feil og gir en positiv opplevelse for brukerne dine.