Lär dig använda React ErrorBoundaries för att smidigt hantera fel, förhindra krascher och skapa en bättre användarupplevelse med robusta återhämtningsstrategier.
React ErrorBoundary: Felisolering och återhämtningsstrategier
I den dynamiska världen av front-end-utveckling, särskilt när man arbetar med komplexa komponentbaserade ramverk som React, är oväntade fel oundvikliga. Dessa fel, om de inte hanteras korrekt, kan leda till applikationskrascher och en frustrerande användarupplevelse. Reacts ErrorBoundary-komponent erbjuder en robust lösning för att smidigt hantera dessa fel, isolera dem och erbjuda återhämtningsstrategier. Denna omfattande guide utforskar kraften i ErrorBoundary och visar hur man effektivt implementerar den för att bygga mer motståndskraftiga och användarvänliga React-applikationer för en global publik.
Förstå behovet av Error Boundaries
Innan vi dyker in i implementeringen, låt oss förstå varför error boundaries är nödvändiga. I React kan fel som inträffar under rendering, i livscykelmetoder eller i konstruktorer för barnkomponenter potentiellt krascha hela applikationen. Detta beror på att ofångade fel propagerar upp i komponentträdet, vilket ofta leder till en blank skärm eller ett föga hjälpsamt felmeddelande. Föreställ dig en användare i Japan som försöker slutföra en viktig finansiell transaktion, bara för att mötas av en blank skärm på grund av ett mindre fel i en till synes orelaterad komponent. Detta illustrerar det kritiska behovet av proaktiv felhantering.
Error boundaries erbjuder ett sätt att fånga JavaScript-fel var som helst i sitt barnkomponentträd, logga dessa fel och visa ett reserv-UI istället för att krascha komponentträdet. De låter dig isolera felaktiga komponenter och förhindra att fel i en del av din applikation påverkar andra, vilket säkerställer en stabilare och mer tillförlitlig användarupplevelse globalt sett.
Vad är en React ErrorBoundary?
En ErrorBoundary är en React-komponent som fångar JavaScript-fel var som helst i sitt barnkomponentträd, loggar dessa fel och visar ett reserv-UI. Det är en klasskomponent som implementerar antingen en eller båda av följande livscykelmetoder:
static getDerivedStateFromError(error): Denna livscykelmetod anropas efter att ett fel har kastats av en underordnad komponent. Den tar emot felet som kastades som ett argument och bör returnera ett värde för att uppdatera komponentens tillstånd.componentDidCatch(error, info): Denna livscykelmetod anropas efter att ett fel har kastats av en underordnad komponent. Den tar emot två argument: felet som kastades och ett info-objekt som innehåller information om vilken komponent som kastade felet. Du kan använda denna metod för att logga felinformation eller utföra andra sidoeffekter.
Skapa en grundläggande ErrorBoundary-komponent
Låt oss skapa en grundläggande ErrorBoundary-komponent för att illustrera de fundamentala principerna.
Kodexempel
Här är koden för en enkel ErrorBoundary-komponent:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
};
}
static getDerivedStateFromError(error) {
// Uppdatera state så att nästa rendering visar reserv-UI:t.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// Exempel "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 också logga felet till en felrapporteringstjänst
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Du kan rendera vilket anpassat reserv-UI som helst
return (
Något gick fel.
Fel: {this.state.error && this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Förklaring
- Konstruktor: Konstruktorn initierar komponentens state med
hasErrorsatt tillfalse. Vi lagrar också error och errorInfo för felsökningsändamål. getDerivedStateFromError(error): Denna statiska metod anropas när ett fel kastas av en barnkomponent. Den uppdaterar state för att indikera att ett fel har inträffat.componentDidCatch(error, info): Denna metod anropas efter att ett fel har kastats. Den tar emot felet och ettinfo-objekt som innehåller information om komponentstacken. Här loggar vi felet till konsolen (ersätt med din föredragna loggningsmekanism, som Sentry, Bugsnag eller en anpassad intern lösning). Vi ställer också in error och errorInfo i state.render(): Render-metoden kontrollerarhasError-state. Om det ärtruerenderar den ett reserv-UI; annars renderar den komponentens barn. Reserv-UI:t bör vara informativt och användarvänligt. Att inkludera feldetaljer och komponentstacken, även om det är till hjälp för utvecklare, bör renderas villkorligt eller tas bort i produktionsmiljöer av säkerhetsskäl.
Använda ErrorBoundary-komponenten
För att använda ErrorBoundary-komponenten, omslut helt enkelt vilken komponent som helst som kan kasta ett fel inom den.
Kodexempel
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
return (
{/* Komponenter som kan kasta ett fel */}
);
}
function App() {
return (
);
}
export default App;
Förklaring
I detta exempel är MyComponent omsluten av ErrorBoundary. Om något fel inträffar inom MyComponent eller dess barn, kommer ErrorBoundary att fånga det och rendera reserv-UI:t.
Avancerade ErrorBoundary-strategier
Även om den grundläggande ErrorBoundary ger en fundamental nivå av felhantering, finns det flera avancerade strategier du kan implementera för att förbättra din felhantering.
1. Granulära Error Boundaries
Istället för att omsluta hela applikationen med en enda ErrorBoundary, överväg att använda granulära error boundaries. Detta innebär att placera ErrorBoundary-komponenter runt specifika delar av din applikation som är mer benägna att få fel eller där ett fel skulle ha en begränsad inverkan. Till exempel kan du omsluta enskilda widgets eller komponenter som förlitar sig på externa datakällor.
Exempel
function ProductList() {
return (
{/* Lista över produkter */}
);
}
function RecommendationWidget() {
return (
{/* Rekommendationsmotor */}
);
}
function App() {
return (
);
}
I detta exempel har RecommendationWidget sin egen ErrorBoundary. Om rekommendationsmotorn misslyckas kommer det inte att påverka ProductList, och användaren kan fortfarande bläddra bland produkterna. Detta granulära tillvägagångssätt förbättrar den övergripande användarupplevelsen genom att isolera fel och förhindra att de sprider sig över applikationen.
2. Felloggning och rapportering
Att logga fel är avgörande för felsökning och identifiering av återkommande problem. Livscykelmetoden componentDidCatch är den idealiska platsen att integrera med felloggningstjänster som Sentry, Bugsnag eller Rollbar. Dessa tjänster tillhandahåller detaljerade felrapporter, inklusive stackspår, användarkontext och miljöinformation, vilket gör att du snabbt kan diagnostisera och lösa problem. Överväg att anonymisera eller redigera känsliga användardata innan du skickar felloggar för att säkerställa efterlevnad av integritetsbestämmelser som GDPR.
Exempel
import * as Sentry from "@sentry/react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(error) {
// Uppdatera state så att nästa rendering visar reserv-UI:t.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// Logga felet till Sentry
Sentry.captureException(error, { extra: info });
// Du kan också logga felet till en felrapporteringstjänst
console.error("Caught an error:", error);
}
render() {
if (this.state.hasError) {
// Du kan rendera vilket anpassat reserv-UI som helst
return (
Något gick fel.
);
}
return this.props.children;
}
}
export default ErrorBoundary;
I detta exempel använder componentDidCatch-metoden Sentry.captureException för att rapportera felet till Sentry. Du kan konfigurera Sentry för att skicka aviseringar till ditt team, vilket gör att du snabbt kan svara på kritiska fel.
3. Anpassat reserv-UI
Reserv-UI:t som visas av ErrorBoundary är en möjlighet att ge en användarvänlig upplevelse även när fel uppstår. Istället för att visa ett generiskt felmeddelande, överväg att visa ett mer informativt meddelande som guidar användaren mot en lösning. Detta kan inkludera instruktioner om hur man uppdaterar sidan, kontaktar support eller försöker igen senare. Du kan också skräddarsy reserv-UI:t baserat på typen av fel som inträffade.
Exempel
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error) {
// Uppdatera state så att nästa rendering visar reserv-UI:t.
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, info) {
console.error("Caught an error:", error);
// Du kan också logga felet till en felrapporteringstjänst
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Du kan rendera vilket anpassat reserv-UI som helst
if (this.state.error instanceof NetworkError) {
return (
Nätverksfel
Vänligen kontrollera din internetanslutning och försök igen.
);
} else {
return (
Något gick fel.
Vänligen försök att uppdatera sidan eller kontakta support.
);
}
}
return this.props.children;
}
}
export default ErrorBoundary;
I detta exempel kontrollerar reserv-UI:t om felet är ett NetworkError. Om det är det, visar det ett specifikt meddelande som instruerar användaren att kontrollera sin internetanslutning. Annars visar det ett generiskt felmeddelande. Att ge specifik, handlingsbar vägledning kan avsevärt förbättra användarupplevelsen.
4. Omprövningsmekanismer
I vissa fall är fel övergående och kan lösas genom att försöka operationen igen. Du kan implementera en omprövningsmekanism inom ErrorBoundary för att automatiskt försöka den misslyckade operationen igen efter en viss fördröjning. Detta kan vara särskilt användbart för att hantera nätverksfel eller tillfälliga serveravbrott. Var försiktig med att implementera omprövningsmekanismer för operationer som kan ha sidoeffekter, eftersom att försöka dem igen kan leda till oavsiktliga konsekvenser.
Exempel
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; // Exponentiell backoff
console.log(`Försöker igen om ${retryDelay / 1000} sekunder...`);
const timer = setTimeout(fetchData, retryDelay);
return () => clearTimeout(timer); // Städa upp timern vid unmount eller omrendering
}
if (!data) {
fetchData();
}
}, [error, retryCount, data]);
if (isLoading) {
return Laddar data...
;
}
if (error) {
return Fel: {error.message} - Försökt igen {retryCount} gånger.
;
}
return Data: {JSON.stringify(data)}
;
}
function App() {
return (
);
}
export default App;
I detta exempel försöker DataFetchingComponent hämta data från ett API. Om ett fel inträffar ökar den retryCount och försöker operationen igen efter en exponentiellt ökande fördröjning. ErrorBoundary fångar alla ohanterade undantag och visar ett felmeddelande, inklusive antalet omprövningsförsök.
5. Error Boundaries och Server-Side Rendering (SSR)
När man använder Server-Side Rendering (SSR) blir felhantering ännu mer kritisk. Fel som inträffar under server-side rendering-processen kan krascha hela servern, vilket leder till driftstopp och en dålig användarupplevelse. Du måste se till att dina error boundaries är korrekt konfigurerade för att fånga fel på både servern och klienten. Ofta har SSR-ramverk som Next.js och Remix sina egna inbyggda felhanteringsmekanismer som kompletterar React Error Boundaries.
6. Testa Error Boundaries
Att testa error boundaries är viktigt för att säkerställa att de fungerar korrekt och ger det förväntade reserv-UI:t. Använd testbibliotek som Jest och React Testing Library för att simulera feltillstånd och verifiera att dina error boundaries fångar felen och renderar lämpligt reserv-UI. Överväg att testa olika typer av fel och kantfall för att säkerställa att dina error boundaries är robusta och hanterar ett brett spektrum av scenarier.
Exempel
import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('Denna komponent kastar ett fel');
return Detta ska inte renderas
;
}
test('renderar reserv-UI när ett fel kastas', () => {
render(
);
const errorMessage = screen.getByText(/Något gick fel/i);
expect(errorMessage).toBeInTheDocument();
});
Detta test renderar en komponent som kastar ett fel inom en ErrorBoundary. Det verifierar sedan att reserv-UI:t renderas korrekt genom att kontrollera om felmeddelandet finns i dokumentet.
7. Graceful Degradation (Elegant nedbrytning)
Error boundaries är en nyckelkomponent för att implementera elegant nedbrytning i dina React-applikationer. Elegant nedbrytning är praxis att designa din applikation så att den fortsätter att fungera, om än med reducerad funktionalitet, även när delar av den misslyckas. Error boundaries låter dig isolera felande komponenter och förhindra att de påverkar resten av applikationen. Genom att tillhandahålla ett reserv-UI och alternativ funktionalitet kan du säkerställa att användare fortfarande kan komma åt väsentliga funktioner även när fel inträffar.
Vanliga fallgropar att undvika
Även om ErrorBoundary är ett kraftfullt verktyg, finns det några vanliga fallgropar att undvika:
- Att inte omsluta asynkron kod:
ErrorBoundaryfångar endast fel under rendering, i livscykelmetoder och i konstruktorer. Fel i asynkron kod (t.ex.setTimeout,Promises) måste fångas medtry...catch-block och hanteras lämpligt inom den asynkrona funktionen. - Överanvändning av Error Boundaries: Undvik att omsluta stora delar av din applikation i en enda
ErrorBoundary. Detta kan göra det svårt att isolera källan till fel och kan leda till att ett generiskt reserv-UI visas för ofta. Använd granulära error boundaries för att isolera specifika komponenter eller funktioner. - Ignorera felinformation: Fånga inte bara fel och visa ett reserv-UI. Se till att logga felinformationen (inklusive komponentstacken) till en felrapporteringstjänst eller din konsol. Detta hjälper dig att diagnostisera och åtgärda de underliggande problemen.
- Visa känslig information i produktion: Undvik att visa detaljerad felinformation (t.ex. stackspår) i produktionsmiljöer. Detta kan exponera känslig information för användare och kan vara en säkerhetsrisk. Visa istället ett användarvänligt felmeddelande och logga den detaljerade informationen till en felrapporteringstjänst.
Error Boundaries med funktionella komponenter och hooks
Även om Error Boundaries implementeras som klasskomponenter kan du fortfarande effektivt använda dem för att hantera fel inom funktionella komponenter som använder hooks. Det typiska tillvägagångssättet innebär att omsluta den funktionella komponenten inom en ErrorBoundary-komponent, som visats tidigare. Felhanteringslogiken ligger inom ErrorBoundary, vilket effektivt isolerar fel som kan uppstå under den funktionella komponentens rendering eller exekvering av hooks.
Specifikt kommer alla fel som kastas under renderingen av den funktionella komponenten eller inom kroppen av en useEffect-hook att fångas av ErrorBoundary. Det är dock viktigt att notera att ErrorBoundaries inte fångar fel som inträffar inom händelsehanterare (t.ex. onClick, onChange) som är kopplade till DOM-element inom den funktionella komponenten. För händelsehanterare bör du fortsätta att använda traditionella try...catch-block för felhantering.
Internationalisering och lokalisering av felmeddelanden
När man utvecklar applikationer för en global publik är det avgörande att internationalisera och lokalisera dina felmeddelanden. Felmeddelanden som visas i ErrorBoundary's reserv-UI bör översättas till användarens föredragna språk för att ge en bättre användarupplevelse. Du kan använda bibliotek som i18next eller React Intl för att hantera dina översättningar och dynamiskt visa lämpligt felmeddelande baserat på användarens locale.
Exempel 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.',
},
},
fr: {
translation: {
'error.generic': 'Une erreur est survenue. Veuillez réessayer plus tard.',
'error.network': 'Erreur réseau. Veuillez vérifier votre connexion Internet.',
},
},
sv: {
translation: {
'error.generic': 'Något gick fel. Vänligen försök igen senare.',
'error.network': 'Nätverksfel. Vänligen kontrollera din internetanslutning.',
},
},
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false, // behövs inte för react då det escapas 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) => {
// Uppdatera state så att nästa rendering visar reserv-UI:t
// return { hasError: true }; // detta fungerar inte med hooks som det är
setHasError(true);
setError(error);
}
if (hasError) {
// Du kan rendera vilket anpassat reserv-UI som helst
return ;
}
return children;
}
export default ErrorBoundary;
I detta exempel använder vi i18next för att hantera översättningar för engelska och franska. ErrorFallback-komponenten använder useTranslation-hooken för att hämta lämpligt felmeddelande baserat på det aktuella språket. Detta säkerställer att användare ser felmeddelanden på sitt föredragna språk, vilket förbättrar den övergripande användarupplevelsen.
Slutsats
React ErrorBoundary-komponenter är ett avgörande verktyg för att bygga robusta och användarvänliga React-applikationer. Genom att implementera error boundaries kan du smidigt hantera fel, förhindra applikationskrascher och ge en bättre användarupplevelse för användare över hela världen. Genom att förstå principerna för error boundaries, implementera avancerade strategier som granulära error boundaries, felloggning och anpassade reserv-UI:er, och undvika vanliga fallgropar, kan du bygga mer motståndskraftiga och tillförlitliga React-applikationer som möter behoven hos en global publik. Kom ihåg att överväga internationalisering och lokalisering när du visar felmeddelanden för att ge en verkligt inkluderande användarupplevelse. I takt med att komplexiteten i webbapplikationer fortsätter att växa, kommer det att bli allt viktigare för utvecklare som bygger högkvalitativ mjukvara att bemästra felhanteringstekniker.