Lær hvordan du bruker React ErrorBoundaries til å håndtere feil på en elegant måte, forhindre krasj i applikasjonen og gi en bedre brukeropplevelse med robuste gjenopprettingsstrategier.
React ErrorBoundary: Feilisolering og gjenopprettingsstrategier
I den dynamiske verdenen av front-end-utvikling, spesielt når man jobber med komplekse komponentbaserte rammeverk som React, er uventede feil uunngåelige. Disse feilene, hvis de ikke håndteres riktig, kan føre til at applikasjonen krasjer og gir en frustrerende brukeropplevelse. Reacts ErrorBoundary-komponent tilbyr en robust løsning for å håndtere disse feilene elegant, isolere dem og tilby gjenopprettingsstrategier. Denne omfattende guiden utforsker kraften i ErrorBoundary og demonstrerer hvordan du effektivt kan implementere den for å bygge mer robuste og brukervennlige React-applikasjoner for et globalt publikum.
Forstå behovet for Error Boundaries
Før vi dykker ned i implementeringen, la oss forstå hvorfor error boundaries er essensielle. I React kan feil som oppstår under rendering, i livssyklusmetoder eller i konstruktører av barnekomponenter, potensielt krasje hele applikasjonen. Dette er fordi feil som ikke fanges opp, propagerer oppover komponenttreet, noe som ofte fører til en blank skjerm eller en lite hjelpsom feilmelding. Se for deg en bruker i Japan som prøver å fullføre en viktig finansiell transaksjon, bare for å møte en blank skjerm på grunn av en mindre feil i en tilsynelatende urelatert komponent. Dette illustrerer det kritiske behovet for proaktiv feilhåndtering.
Error boundaries gir en måte å fange JavaScript-feil hvor som helst i sitt barnekomponenttre, logge disse feilene og vise et reserve-UI i stedet for å krasje komponenttreet. De lar deg isolere feilaktige komponenter og forhindre at feil i én del av applikasjonen påvirker andre, noe som sikrer en mer stabil og pålitelig brukeropplevelse globalt.
Hva er en React ErrorBoundary?
En ErrorBoundary er en React-komponent som fanger JavaScript-feil hvor som helst i sitt barnekomponenttre, logger disse feilene og viser et reserve-UI. Det er en klassekomponent som implementerer én eller begge av følgende livssyklusmetoder:
static getDerivedStateFromError(error): Denne livssyklusmetoden kalles etter at en feil har blitt kastet av en etterkommerkomponent. Den mottar feilen som ble kastet som et argument og skal returnere en verdi for å oppdatere komponentens state.componentDidCatch(error, info): Denne livssyklusmetoden kalles etter at en feil har blitt kastet av en etterkommerkomponent. Den mottar to argumenter: feilen som ble kastet og et info-objekt som inneholder informasjon om hvilken komponent som kastet feilen. Du kan bruke denne metoden til å logge feilinformasjon eller utføre andre sideeffekter.
Opprette en grunnleggende ErrorBoundary-komponent
La oss lage en grunnleggende ErrorBoundary-komponent for å illustrere de fundamentale prinsippene.
Kodeeksempel
Her er koden for en enkel ErrorBoundary-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 rendering vil vise reserve-UI-et.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// Eksempel "componentStack":
// in ComponentThatThrows (created by App)
// in App
console.error("Caught an error:", error);
console.error("Error info:", info.componentStack);
this.setState({ error: error, errorInfo: info });
// 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 (
Noe gikk galt.
Feil: {this.state.error && this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Forklaring
- Konstruktør: Konstruktøren initialiserer komponentens state med
hasErrorsatt tilfalse. Vi lagrer også feilen og errorInfo for feilsøkingsformål. getDerivedStateFromError(error): Denne statiske metoden kalles når en feil blir kastet av en barnekomponent. Den oppdaterer state for å indikere at en feil har oppstått.componentDidCatch(error, info): Denne metoden kalles etter at en feil er kastet. Den mottar feilen og etinfo-objekt som inneholder informasjon om komponentstakken. Her logger vi feilen til konsollen (erstatt med din foretrukne loggemekanisme, som Sentry, Bugsnag eller en egen intern løsning). Vi setter også feilen og errorInfo i state.render(): Render-metoden sjekkerhasError-state. Hvis den ertrue, renderes et reserve-UI; ellers renderes komponentens barn. Reserve-UI-et bør være informativt og brukervennlig. Å inkludere feildetaljer og komponentstakken, selv om det er nyttig for utviklere, bør renderes betinget eller fjernes i produksjonsmiljøer av sikkerhetsgrunner.
Bruke ErrorBoundary-komponenten
For å bruke ErrorBoundary-komponenten, pakk enkelt og greit inn en hvilken som helst komponent som kan kaste en feil i den.
Kodeeksempel
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
return (
{/* Komponenter som kan kaste en feil */}
);
}
function App() {
return (
);
}
export default App;
Forklaring
I dette eksempelet er MyComponent pakket inn i ErrorBoundary. Hvis en feil oppstår i MyComponent eller dens barn, vil ErrorBoundary fange den og rendere reserve-UI-et.
Avanserte strategier for ErrorBoundary
Selv om den grunnleggende ErrorBoundary gir et fundamentalt nivå av feilhåndtering, er det flere avanserte strategier du kan implementere for å forbedre feilbehandlingen din.
1. Granulære Error Boundaries
I stedet for å pakke hele applikasjonen inn i en enkelt ErrorBoundary, bør du vurdere å bruke granulære error boundaries. Dette innebærer å plassere ErrorBoundary-komponenter rundt spesifikke deler av applikasjonen som er mer utsatt for feil, eller hvor en feil ville ha begrenset innvirkning. For eksempel kan du pakke inn individuelle widgets eller komponenter som er avhengige av eksterne datakilder.
Eksempel
function ProductList() {
return (
{/* Liste over produkter */}
);
}
function RecommendationWidget() {
return (
{/* Anbefalingsmotor */}
);
}
function App() {
return (
);
}
I dette eksempelet har RecommendationWidget sin egen ErrorBoundary. Hvis anbefalingsmotoren feiler, vil det ikke påvirke ProductList, og brukeren kan fortsatt bla gjennom produkter. Denne granulære tilnærmingen forbedrer den totale brukeropplevelsen ved å isolere feil og forhindre at de sprer seg gjennom applikasjonen.
2. Feillogging og rapportering
Logging av feil er avgjørende for feilsøking og identifisering av gjentakende problemer. Livssyklusmetoden componentDidCatch er det ideelle stedet å integrere med feilloggingstjenester som Sentry, Bugsnag eller Rollbar. Disse tjenestene gir detaljerte feilrapporter, inkludert stack traces, brukerkontekst og miljøinformasjon, slik at du raskt kan diagnostisere og løse problemer. Vurder å anonymisere eller redigere sensitiv brukerdata før du sender feillogger for å sikre samsvar med personvernforskrifter som GDPR.
Eksempel
import * as Sentry from "@sentry/react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(error) {
// Oppdater state slik at neste rendering vil vise reserve-UI-et.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// Logg feilen til Sentry
Sentry.captureException(error, { extra: info });
// Du kan også logge feilen til en feilrapporteringstjeneste
console.error("Caught an error:", error);
}
render() {
if (this.state.hasError) {
// Du kan rendere hvilket som helst tilpasset reserve-UI
return (
Noe gikk galt.
);
}
return this.props.children;
}
}
export default ErrorBoundary;
I dette eksempelet bruker componentDidCatch-metoden Sentry.captureException for å rapportere feilen til Sentry. Du kan konfigurere Sentry til å sende varsler til teamet ditt, slik at dere kan respondere raskt på kritiske feil.
3. Tilpasset reserve-UI
Reserve-UI-et som vises av ErrorBoundary er en mulighet til å gi en brukervennlig opplevelse selv når feil oppstår. I stedet for å vise en generisk feilmelding, bør du vurdere å vise en mer informativ melding som veileder brukeren mot en løsning. Dette kan inkludere instruksjoner om hvordan man oppdaterer siden, kontakter support eller prøver igjen senere. Du kan også skreddersy reserve-UI-et basert på typen feil som oppsto.
Eksempel
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error) {
// Oppdater state slik at neste rendering vil vise reserve-UI-et.
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, info) {
console.error("Caught an error:", error);
// 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
if (this.state.error instanceof NetworkError) {
return (
Nettverksfeil
Vennligst sjekk internettforbindelsen din og prøv igjen.
);
} else {
return (
Noe gikk galt.
Vennligst prøv å oppdatere siden eller kontakt support.
);
}
}
return this.props.children;
}
}
export default ErrorBoundary;
I dette eksempelet sjekker reserve-UI-et om feilen er en NetworkError. Hvis den er det, vises en spesifikk melding som instruerer brukeren til å sjekke internettforbindelsen. Ellers vises en generisk feilmelding. Å gi spesifikk, handlingsrettet veiledning kan i stor grad forbedre brukeropplevelsen.
4. Mekanismer for gjentatte forsøk
I noen tilfeller er feil forbigående og kan løses ved å prøve operasjonen på nytt. Du kan implementere en mekanisme for gjentatte forsøk i ErrorBoundary for å automatisk prøve den mislykkede operasjonen på nytt etter en viss forsinkelse. Dette kan være spesielt nyttig for å håndtere nettverksfeil eller midlertidige serverbrudd. Vær forsiktig med å implementere gjentaksforsøksmekanismer for operasjoner som kan ha sideeffekter, da det å prøve dem på nytt kan føre til utilsiktede konsekvenser.
Eksempel
import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [retryCount, setRetryCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (e) {
setError(e);
setRetryCount(prevCount => prevCount + 1);
} finally {
setIsLoading(false);
}
};
if (error && retryCount < 3) {
const retryDelay = Math.pow(2, retryCount) * 1000; // Eksponentiell backoff
console.log(`Prøver igjen om ${retryDelay / 1000} sekunder...`);
const timer = setTimeout(fetchData, retryDelay);
return () => clearTimeout(timer); // Rydd opp timer ved unmount eller re-render
}
if (!data) {
fetchData();
}
}, [error, retryCount, data]);
if (isLoading) {
return Laster data...
;
}
if (error) {
return Feil: {error.message} - Forsøkt på nytt {retryCount} ganger.
;
}
return Data: {JSON.stringify(data)}
;
}
function App() {
return (
);
}
export default App;
I dette eksempelet prøver DataFetchingComponent å hente data fra et API. Hvis en feil oppstår, økes retryCount, og operasjonen prøves på nytt etter en eksponentielt økende forsinkelse. ErrorBoundary fanger eventuelle uhåndterte unntak og viser en feilmelding, inkludert antall gjentatte forsøk.
5. Error Boundaries og Server-Side Rendering (SSR)
Når man bruker Server-Side Rendering (SSR), blir feilhåndtering enda mer kritisk. Feil som oppstår under server-side rendering-prosessen kan krasje hele serveren, noe som fører til nedetid og en dårlig brukeropplevelse. Du må sørge for at dine error boundaries er riktig konfigurert for å fange feil både på serveren og klienten. Ofte har SSR-rammeverk som Next.js og Remix sine egne innebygde feilhåndteringsmekanismer som komplementerer React Error Boundaries.
6. Testing av Error Boundaries
Testing av error boundaries er essensielt for å sikre at de fungerer korrekt og gir det forventede reserve-UI-et. Bruk testbiblioteker som Jest og React Testing Library for å simulere feiltilstander og verifisere at dine error boundaries fanger feilene og render den riktige reserve-UI-en. Vurder å teste forskjellige typer feil og kanttilfeller for å sikre at dine error boundaries er robuste og håndterer et bredt spekter av scenarier.
Eksempel
import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('This component throws an error');
return This should not be rendered
;
}
test('renders fallback UI when an error is thrown', () => {
render(
);
const errorMessage = screen.getByText(/Noe gikk galt/i);
expect(errorMessage).toBeInTheDocument();
});
Denne testen render en komponent som kaster en feil innenfor en ErrorBoundary. Den verifiserer deretter at reserve-UI-et renderes korrekt ved å sjekke om feilmeldingen er til stede i dokumentet.
7. Elegant degradering
Error boundaries er en nøkkelkomponent for å implementere elegant degradering i dine React-applikasjoner. Elegant degradering er praksisen med å designe applikasjonen din til å fortsette å fungere, om enn med redusert funksjonalitet, selv når deler av den feiler. Error boundaries lar deg isolere feilende komponenter og forhindre at de påvirker resten av applikasjonen. Ved å tilby et reserve-UI og alternativ funksjonalitet, kan du sikre at brukere fortsatt har tilgang til essensielle funksjoner selv når feil oppstår.
Vanlige fallgruver å unngå
Selv om ErrorBoundary er et kraftig verktøy, er det noen vanlige fallgruver å unngå:
- Ikke pakke inn asynkron kode:
ErrorBoundaryfanger kun feil under rendering, i livssyklusmetoder og i konstruktører. Feil i asynkron kode (f.eks.setTimeout,Promises) må fanges medtry...catch-blokker og håndteres på passende måte i den asynkrone funksjonen. - Overforbruk av Error Boundaries: Unngå å pakke store deler av applikasjonen din inn i en enkelt
ErrorBoundary. Dette kan gjøre det vanskelig å isolere kilden til feil og kan føre til at et generisk reserve-UI vises for ofte. Bruk granulære error boundaries for å isolere spesifikke komponenter eller funksjoner. - Ignorere feilinformasjon: Ikke bare fang feil og vis et reserve-UI. Sørg for å logge feilinformasjonen (inkludert komponentstakken) til en feilrapporteringstjeneste eller konsollen din. Dette vil hjelpe deg med å diagnostisere og fikse de underliggende problemene.
- Vise sensitiv informasjon i produksjon: Unngå å vise detaljert feilinformasjon (f.eks. stack traces) i produksjonsmiljøer. Dette kan eksponere sensitiv informasjon for brukere og kan være en sikkerhetsrisiko. Vis i stedet en brukervennlig feilmelding og logg den detaljerte informasjonen til en feilrapporteringstjeneste.
Error Boundaries med funksjonelle komponenter og Hooks
Selv om Error Boundaries implementeres som klassekomponenter, kan du fortsatt effektivt bruke dem til å håndtere feil i funksjonelle komponenter som bruker hooks. Den typiske tilnærmingen innebærer å pakke den funksjonelle komponenten inn i en ErrorBoundary-komponent, som demonstrert tidligere. Feilhåndteringslogikken ligger i ErrorBoundary, som effektivt isolerer feil som kan oppstå under renderingen av den funksjonelle komponenten eller utførelsen av hooks.
Spesifikt vil alle feil som kastes under renderingen av den funksjonelle komponenten eller i kroppen av en useEffect-hook, bli fanget av ErrorBoundary. Det er imidlertid viktig å merke seg at ErrorBoundaries ikke fanger feil som oppstår i hendelseshåndterere (f.eks. onClick, onChange) knyttet til DOM-elementer i den funksjonelle komponenten. For hendelseshåndterere bør du fortsette å bruke tradisjonelle try...catch-blokker for feilhåndtering.
Internasjonalisering og lokalisering av feilmeldinger
Når man utvikler applikasjoner for et globalt publikum, er det avgjørende å internasjonalisere og lokalisere feilmeldingene dine. Feilmeldinger som vises i ErrorBoundarys reserve-UI bør oversettes til brukerens foretrukne språk for å gi en bedre brukeropplevelse. Du kan bruke biblioteker som i18next eller React Intl for å administrere oversettelsene dine og dynamisk vise den riktige feilmeldingen basert på brukerens lokalitet.
Eksempel med i18next
import i18next from 'i18next';
import { useTranslation } from 'react-i18next';
i18next.init({
resources: {
en: {
translation: {
'error.generic': 'Something went wrong. Please try again later.',
'error.network': 'Network error. Please check your internet connection.',
},
},
no: {
translation: {
'error.generic': 'Noe gikk galt. Vennligst prøv igjen senere.',
'error.network': 'Nettverksfeil. Vennligst sjekk internettforbindelsen din.',
},
},
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false, // ikke nødvendig for react, da det escaper som standard
},
});
function ErrorFallback({ error }) {
const { t } = useTranslation();
let errorMessageKey = 'error.generic';
if (error instanceof NetworkError) {
errorMessageKey = 'error.network';
}
return (
{t('error.generic')}
{t(errorMessageKey)}
);
}
function ErrorBoundary({ children }) {
const [hasError, setHasError] = useState(false);
const [error, setError] = useState(null);
static getDerivedStateFromError = (error) => {
// Oppdater state slik at neste rendering vil vise reserve-UI-et
// return { hasError: true }; // dette fungerer ikke med hooks slik det er skrevet
setHasError(true);
setError(error);
}
if (hasError) {
// Du kan rendere hvilket som helst tilpasset reserve-UI
return ;
}
return children;
}
export default ErrorBoundary;
I dette eksempelet bruker vi i18next til å håndtere oversettelser for engelsk og norsk. ErrorFallback-komponenten bruker useTranslation-hooken for å hente den riktige feilmeldingen basert på gjeldende språk. Dette sikrer at brukere ser feilmeldinger på sitt foretrukne språk, noe som forbedrer den totale brukeropplevelsen.
Konklusjon
React ErrorBoundary-komponenter er et avgjørende verktøy for å bygge robuste og brukervennlige React-applikasjoner. Ved å implementere error boundaries kan du elegant håndtere feil, forhindre applikasjonskrasj og gi en bedre brukeropplevelse for brukere over hele verden. Ved å forstå prinsippene bak error boundaries, implementere avanserte strategier som granulære error boundaries, feillogging og tilpassede reserve-UIer, og unngå vanlige fallgruver, kan du bygge mer motstandsdyktige og pålitelige React-applikasjoner som møter behovene til et globalt publikum. Husk å vurdere internasjonalisering og lokalisering når du viser feilmeldinger for å gi en virkelig inkluderende brukeropplevelse. Ettersom kompleksiteten i webapplikasjoner fortsetter å vokse, vil mestring av feilhåndteringsteknikker bli stadig viktigere for utviklere som bygger programvare av høy kvalitet.