Een uitgebreide gids voor het begrijpen en implementeren van JavaScript Error Boundaries in React voor robuuste foutafhandeling en sierlijke UI-degradatie.
JavaScript Error Boundary: Een Gids voor Implementatie van Foutafhandeling in React
In de wereld van React-ontwikkeling kunnen onverwachte fouten leiden tot frustrerende gebruikerservaringen en instabiliteit van de applicatie. Een goed gedefinieerde strategie voor foutafhandeling is cruciaal voor het bouwen van robuuste en betrouwbare applicaties. React's Error Boundaries bieden een krachtig mechanisme om fouten die optreden binnen uw componentenboom netjes af te handelen, waardoor wordt voorkomen dat de hele applicatie crasht en u een fallback-UI kunt weergeven.
Wat is een Error Boundary?
Een Error Boundary is een React-component dat JavaScript-fouten overal in zijn onderliggende componentenboom opvangt, deze fouten logt en een fallback-UI weergeeft in plaats van de gecrashte componentenboom. Error Boundaries vangen fouten op tijdens het renderen, in lifecycle-methoden en in constructors van de hele boom eronder.
Zie een Error Boundary als een try...catch
-blok voor React-componenten. Net zoals een try...catch
-blok u in staat stelt om excepties in synchrone JavaScript-code af te handelen, stelt een Error Boundary u in staat om fouten af te handelen die optreden tijdens het renderen van uw React-componenten.
Belangrijke opmerking: Error Boundaries vangen geen fouten op voor:
- Event handlers (leer meer in de volgende secties)
- Asynchrone code (bijv.
setTimeout
ofrequestAnimationFrame
callbacks) - Server-side rendering
- Fouten die in de Error Boundary zelf worden gegenereerd (in plaats van in zijn kinderen)
Waarom Error Boundaries gebruiken?
Het gebruik van Error Boundaries biedt verschillende belangrijke voordelen:
- Verbeterde Gebruikerservaring: In plaats van een leeg wit scherm of een cryptische foutmelding, kunt u een gebruiksvriendelijke fallback-UI tonen die de gebruiker informeert dat er iets mis is gegaan en mogelijk een herstelmogelijkheid biedt (bijv. de pagina herladen of naar een andere sectie navigeren).
- Applicatiestabiliteit: Error Boundaries voorkomen dat fouten in één deel van uw applicatie de hele applicatie laten crashen. Dit is met name belangrijk voor complexe applicaties met veel onderling verbonden componenten.
- Gecentraliseerde Foutafhandeling: Error Boundaries bieden een centrale locatie om fouten te loggen en de oorzaak van problemen op te sporen. Dit vereenvoudigt het debuggen en onderhoud.
- Sierlijke Degradatie: U kunt Error Boundaries strategisch rond verschillende delen van uw applicatie plaatsen om ervoor te zorgen dat zelfs als sommige componenten falen, de rest van de applicatie functioneel blijft. Dit maakt sierlijke degradatie mogelijk bij fouten.
Error Boundaries Implementeren in React
Om een Error Boundary te maken, moet u een class-component definiëren die een (of beide) van de volgende lifecycle-methoden implementeert:
static getDerivedStateFromError(error)
: Deze lifecycle-methode wordt aangeroepen nadat een fout is gegenereerd door een onderliggend component. Het ontvangt de gegenereerde fout als argument en moet een waarde retourneren om de state van het component bij te werken om aan te geven dat er een fout is opgetreden (bijv. het instellen van eenhasError
-vlag optrue
).componentDidCatch(error, info)
: Deze lifecycle-methode wordt aangeroepen nadat een fout is gegenereerd door een onderliggend component. Het ontvangt de gegenereerde fout als argument, samen met eeninfo
-object dat informatie bevat over welk component de fout heeft veroorzaakt. U kunt deze methode gebruiken om de fout te loggen naar een service zoals Sentry of Bugsnag.
Hier is een basisvoorbeeld van een Error Boundary-component:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Werk de state bij zodat de volgende render de fallback-UI toont.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, info) {
// Voorbeeld "componentStack":
// in ComponentThatThrows (created by App)
// in MyErrorBoundary (created by App)
// in div (created by App)
// in App
console.error("Caught an error:", error, info);
this.setState({
errorInfo: info.componentStack
});
// U kunt de fout ook loggen naar een foutrapportageservice
//logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// U kunt elke aangepaste fallback-UI renderen
return (
<div>
<h2>Er is iets misgegaan.</h2>
<p>Fout: {this.state.error ? this.state.error.message : "Er is een onbekende fout opgetreden."}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.errorInfo && this.state.errorInfo}
</details>
</div>
);
}
return this.props.children;
}
}
Om de Error Boundary te gebruiken, wikkelt u eenvoudig de componentenboom die u wilt beschermen:
<ErrorBoundary>
<MyComponentThatMightThrow/>
</ErrorBoundary>
Praktische Voorbeelden van het Gebruik van Error Boundary
Laten we enkele praktische scenario's bekijken waarin Error Boundaries bijzonder nuttig kunnen zijn:
1. API-fouten Afhandelen
Bij het ophalen van gegevens van een API kunnen fouten optreden door netwerkproblemen, serverproblemen of ongeldige gegevens. U kunt het component dat de gegevens ophaalt en weergeeft, omwikkelen met een Error Boundary om deze fouten netjes af te handelen.
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) {
// Fout wordt opgevangen door de ErrorBoundary
throw error;
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) {
return <p>Gebruikersprofiel laden...</p>;
}
if (!user) {
return <p>Geen gebruikersgegevens beschikbaar.</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
function App() {
return (
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
);
}
In dit voorbeeld, als de API-aanroep mislukt of een fout retourneert, zal de Error Boundary de fout opvangen en een fallback-UI weergeven (gedefinieerd binnen de render
-methode van de Error Boundary). Dit voorkomt dat de hele applicatie crasht en geeft de gebruiker een meer informatieve boodschap. U zou de fallback-UI kunnen uitbreiden met een optie om de aanvraag opnieuw te proberen.
2. Fouten van Externe Bibliotheken Afhandelen
Bij het gebruik van externe bibliotheken is het mogelijk dat ze onverwachte fouten genereren. Het omwikkelen van componenten die deze bibliotheken gebruiken met Error Boundaries kan u helpen deze fouten netjes af te handelen.
Neem een hypothetische grafiekbibliotheek die af en toe fouten genereert door data-inconsistenties of andere problemen. U zou het grafiekcomponent als volgt kunnen omwikkelen:
function MyChartComponent() {
try {
// Render de grafiek met behulp van de externe bibliotheek
return <Chart data={data} />;
} catch (error) {
// Dit catch-blok is niet effectief voor fouten in de lifecycle van React-componenten
// Het is voornamelijk voor synchrone fouten binnen deze specifieke functie.
console.error("Error rendering chart:", error);
// Overweeg de fout te genereren om te worden opgevangen door ErrorBoundary
throw error; // De fout opnieuw genereren
}
}
function App() {
return (
<ErrorBoundary>
<MyChartComponent />
</ErrorBoundary>
);
}
Als het Chart
-component een fout genereert, zal de Error Boundary deze opvangen en een fallback-UI weergeven. Merk op dat de try/catch binnen MyChartComponent alleen fouten binnen de synchrone functie zal opvangen, niet de lifecycle van het component. Daarom is de ErrorBoundary hier cruciaal.
3. Renderfouten Afhandelen
Fouten kunnen optreden tijdens het renderproces door ongeldige gegevens, onjuiste prop-types of andere problemen. Error Boundaries kunnen deze fouten opvangen en voorkomen dat de applicatie crasht.
function DisplayName({ name }) {
if (typeof name !== 'string') {
throw new Error('Naam moet een string zijn');
}
return <h2>Hallo, {name}!</h2>;
}
function App() {
return (
<ErrorBoundary>
<DisplayName name={123} /> <!-- Onjuist prop-type -->
</ErrorBoundary>
);
}
In dit voorbeeld verwacht het DisplayName
-component dat de name
-prop een string is. Als in plaats daarvan een getal wordt doorgegeven, wordt er een fout gegenereerd en zal de Error Boundary deze opvangen en een fallback-UI weergeven.
Error Boundaries en Event Handlers
Zoals eerder vermeld, vangen Error Boundaries geen fouten op die optreden binnen event handlers. Dit komt doordat event handlers doorgaans asynchroon zijn, en Error Boundaries vangen alleen fouten op die optreden tijdens het renderen, in lifecycle-methoden en in constructors.
Om fouten in event handlers af te handelen, moet u een traditioneel try...catch
-blok gebruiken binnen de event handler-functie.
function MyComponent() {
const handleClick = () => {
try {
// Code die mogelijk een fout kan genereren
throw new Error('Er is een fout opgetreden in de event handler');
} catch (error) {
console.error('Fout opgevangen in de event handler:', error);
// Handel de fout af (bijv. een foutmelding aan de gebruiker tonen)
}
};
return <button onClick={handleClick}>Klik op mij</button>;
}
Globale Foutafhandeling
Hoewel Error Boundaries uitstekend zijn voor het afhandelen van fouten binnen de React-componentenboom, dekken ze niet alle mogelijke foutscenario's. Ze vangen bijvoorbeeld geen fouten op die buiten React-componenten optreden, zoals fouten in globale event listeners of fouten in code die wordt uitgevoerd voordat React is geïnitialiseerd.
Om dit soort fouten af te handelen, kunt u de window.onerror
event handler gebruiken.
window.onerror = function(message, source, lineno, colno, error) {
console.error('Globale foutafhandelaar:', message, source, lineno, colno, error);
// Log de fout naar een service zoals Sentry of Bugsnag
// Toon een globale foutmelding aan de gebruiker (optioneel)
return true; // Voorkom het standaardgedrag voor foutafhandeling
};
De window.onerror
event handler wordt aangeroepen telkens wanneer een niet-opgevangen JavaScript-fout optreedt. U kunt deze gebruiken om de fout te loggen, een globale foutmelding aan de gebruiker te tonen, of andere acties te ondernemen om de fout af te handelen.
Belangrijk: Het retourneren van true
vanuit de window.onerror
event handler voorkomt dat de browser de standaardfoutmelding weergeeft. Wees echter bedacht op de gebruikerservaring; als u de standaardmelding onderdrukt, zorg er dan voor dat u een duidelijk en informatief alternatief biedt.
Best Practices voor het Gebruik van Error Boundaries
Hier zijn enkele best practices om in gedachten te houden bij het gebruik van Error Boundaries:
- Plaats Error Boundaries strategisch: Omwikkel verschillende delen van uw applicatie met Error Boundaries om fouten te isoleren en te voorkomen dat ze zich voortplanten. Overweeg om hele routes of belangrijke secties van uw UI te omwikkelen.
- Bied een informatieve fallback-UI: De fallback-UI moet de gebruiker informeren dat er een fout is opgetreden en mogelijk een herstelmogelijkheid bieden. Vermijd het tonen van generieke foutmeldingen zoals "Er is iets misgegaan."
- Log fouten: Gebruik de
componentDidCatch
lifecycle-methode om fouten te loggen naar een service zoals Sentry of Bugsnag. Dit helpt u de oorzaak van problemen op te sporen en de stabiliteit van uw applicatie te verbeteren. - Gebruik Error Boundaries niet voor verwachte fouten: Error Boundaries zijn ontworpen om onverwachte fouten af te handelen. Voor verwachte fouten (bijv. validatiefouten, API-fouten), gebruik specifiekere mechanismen voor foutafhandeling, zoals
try...catch
-blokken of aangepaste componenten voor foutafhandeling. - Overweeg meerdere niveaus van Error Boundaries: U kunt Error Boundaries nesten om verschillende niveaus van foutafhandeling te bieden. U kunt bijvoorbeeld een globale Error Boundary hebben die alle niet-afgehandelde fouten opvangt en een generieke foutmelding weergeeft, en specifiekere Error Boundaries die fouten in bepaalde componenten opvangen en gedetailleerdere foutmeldingen tonen.
- Vergeet server-side rendering niet: Als u server-side rendering gebruikt, moet u fouten ook op de server afhandelen. Error Boundaries werken op de server, maar u moet mogelijk extra mechanismen voor foutafhandeling gebruiken om fouten op te vangen die tijdens de initiële render optreden.
Geavanceerde Error Boundary Technieken
1. Een Render Prop Gebruiken
In plaats van een statische fallback-UI te renderen, kunt u een render prop gebruiken voor meer flexibiliteit in de manier waarop fouten worden afgehandeld. Een render prop is een functie-prop die een component gebruikt om iets te renderen.
class ErrorBoundary extends React.Component {
// ... (hetzelfde als voorheen)
render() {
if (this.state.hasError) {
// Gebruik de render prop om de fallback-UI te renderen
return this.props.fallbackRender(this.state.error, this.state.errorInfo);
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary fallbackRender={(error, errorInfo) => (
<div>
<h2>Er is iets misgegaan!</h2>
<p>Fout: {error.message}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{errorInfo.componentStack}
</details>
</div>
)}>
<MyComponentThatMightThrow/>
</ErrorBoundary>
);
}
Hiermee kunt u de fallback-UI per Error Boundary aanpassen. De fallbackRender
-prop ontvangt de fout en foutinformatie als argumenten, waardoor u specifiekere foutmeldingen kunt weergeven of andere acties kunt ondernemen op basis van de fout.
2. Error Boundary als een Higher-Order Component (HOC)
U kunt een higher-order component (HOC) maken dat een ander component omwikkelt met een Error Boundary. Dit kan handig zijn om Error Boundaries op meerdere componenten toe te passen zonder dezelfde code te hoeven herhalen.
function withErrorBoundary(WrappedComponent) {
return class WithErrorBoundary extends React.Component {
render() {
return (
<ErrorBoundary>
<WrappedComponent {...this.props} />
</ErrorBoundary>
);
}
};
}
// Gebruik:
const MyComponentWithErrorHandling = withErrorBoundary(MyComponentThatMightThrow);
De withErrorBoundary
-functie neemt een component als argument en retourneert een nieuw component dat het oorspronkelijke component omwikkelt met een Error Boundary. Hiermee kunt u eenvoudig foutafhandeling toevoegen aan elk component in uw applicatie.
Error Boundaries Testen
Het is belangrijk om uw Error Boundaries te testen om ervoor te zorgen dat ze correct werken. U kunt testbibliotheken zoals Jest en React Testing Library gebruiken om uw Error Boundaries te testen.
Hier is een voorbeeld van hoe u een Error Boundary kunt testen met React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('Dit component genereert een fout');
}
test('rendert fallback-UI wanneer een fout wordt gegenereerd', () => {
render(
<ErrorBoundary>
<ComponentThatThrows />
</ErrorBoundary>
);
expect(screen.getByText('Er is iets misgegaan.')).toBeInTheDocument();
});
Deze test rendert het ComponentThatThrows
-component, dat een fout genereert. De test beweert vervolgens dat de fallback-UI die door de Error Boundary wordt gerenderd, wordt weergegeven.
Error Boundaries en Server Components (React 18+)
Met de introductie van Server Components in React 18 en later, blijven Error Boundaries een vitale rol spelen in foutafhandeling. Server Components worden uitgevoerd op de server en sturen alleen de gerenderde output naar de client. Hoewel de kernprincipes hetzelfde blijven, zijn er een paar nuances om te overwegen:
- Server-side Foutlogging: Zorg ervoor dat u fouten die optreden binnen Server Components op de server logt. Dit kan het gebruik van een server-side logging framework inhouden of het verzenden van fouten naar een foutopsporingsdienst.
- Client-side Fallback: Ook al renderen Server Components op de server, u moet nog steeds een client-side fallback-UI bieden in geval van fouten. Dit zorgt ervoor dat de gebruiker een consistente ervaring heeft, zelfs als de server het component niet kan renderen.
- Streaming SSR: Bij het gebruik van streaming Server-Side Rendering (SSR) kunnen fouten optreden tijdens het streamingproces. Error Boundaries kunnen u helpen deze fouten netjes af te handelen door een fallback-UI te renderen voor de betreffende stream.
Foutafhandeling in Server Components is een gebied in ontwikkeling, dus het is belangrijk om op de hoogte te blijven van de nieuwste best practices en aanbevelingen.
Veelvoorkomende Valkuilen om te Vermijden
- Te veel vertrouwen op Error Boundaries: Gebruik Error Boundaries niet als vervanging voor de juiste foutafhandeling in uw componenten. Streef er altijd naar om robuuste en betrouwbare code te schrijven die fouten netjes afhandelt.
- Fouten Negeren: Zorg ervoor dat u fouten logt die door Error Boundaries worden opgevangen, zodat u de oorzaak van problemen kunt achterhalen. Toon niet zomaar een fallback-UI en negeer de fout.
- Error Boundaries gebruiken voor Validatiefouten: Error Boundaries zijn niet het juiste hulpmiddel voor het afhandelen van validatiefouten. Gebruik in plaats daarvan specifiekere validatietechnieken.
- Error Boundaries niet Testen: Test uw Error Boundaries om er zeker van te zijn dat ze correct werken.
Conclusie
Error Boundaries zijn een krachtig hulpmiddel voor het bouwen van robuuste en betrouwbare React-applicaties. Door te begrijpen hoe u Error Boundaries effectief kunt implementeren en gebruiken, kunt u de gebruikerservaring verbeteren, applicatiecrashes voorkomen en het debuggen vereenvoudigen. Onthoud dat u Error Boundaries strategisch moet plaatsen, een informatieve fallback-UI moet bieden, fouten moet loggen en uw Error Boundaries grondig moet testen.
Door de richtlijnen en best practices in deze gids te volgen, kunt u ervoor zorgen dat uw React-applicaties bestand zijn tegen fouten en een positieve ervaring bieden voor uw gebruikers.