Nederlands

Beheers React Error Boundaries voor het bouwen van veerkrachtige en gebruiksvriendelijke applicaties. Leer best practices, implementatietechnieken en geavanceerde strategieën voor foutafhandeling.

React Error Boundaries: Gracieuze technieken voor foutafhandeling voor robuuste applicaties

In de dynamische wereld van webontwikkeling is het creëren van robuuste en gebruiksvriendelijke applicaties van het grootste belang. React, een populaire JavaScript-bibliotheek voor het bouwen van gebruikersinterfaces, biedt een krachtig mechanisme voor het op gracieuze wijze afhandelen van fouten: Error Boundaries. Deze uitgebreide gids duikt in het concept van Error Boundaries en verkent hun doel, implementatie en best practices voor het bouwen van veerkrachtige React-applicaties.

De noodzaak van Error Boundaries begrijpen

React-componenten, zoals alle code, zijn gevoelig voor fouten. Deze fouten kunnen afkomstig zijn van verschillende bronnen, waaronder:

Zonder goede foutafhandeling kan een fout in een React-component de hele applicatie laten crashen, wat resulteert in een slechte gebruikerservaring. Error Boundaries bieden een manier om deze fouten op te vangen en te voorkomen dat ze zich voortplanten in de componentenboom, waardoor wordt gewaarborgd dat de applicatie functioneel blijft, zelfs wanneer afzonderlijke componenten falen.

Wat zijn React Error Boundaries?

Error Boundaries zijn React-componenten die JavaScript-fouten overal in hun kindcomponentenboom opvangen, die fouten loggen en een fallback-UI weergeven in plaats van de componentenboom die is gecrasht. Ze fungeren als een vangnet en voorkomen dat fouten de hele applicatie laten crashen.

Belangrijkste kenmerken van Error Boundaries:

Error Boundaries implementeren

Laten we de procedure doornemen voor het maken van een basis Error Boundary-component:

1. Het Error Boundary-component maken

Maak eerst een nieuw klassecomponent, bijvoorbeeld met de naam ErrorBoundary:


import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return {
      hasError: true
    };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    console.error("Caught error: ", error, errorInfo);
    // Example: logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (
        <div>
          <h2>Er is iets fout gegaan.</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;

Uitleg:

2. De Error Boundary gebruiken

Om de Error Boundary te gebruiken, omwikkelt u gewoon elke component die een fout kan veroorzaken met het ErrorBoundary-component:


import ErrorBoundary from './ErrorBoundary';

function MyComponent() {
  // This component might throw an error
  return (
    <ErrorBoundary>
      <PotentiallyBreakingComponent />
    </ErrorBoundary>
  );
}

export default MyComponent;

Als PotentiallyBreakingComponent een fout veroorzaakt, zal de ErrorBoundary deze opvangen, de fout loggen en de fallback-UI weergeven.

3. Illustratieve voorbeelden met globale context

Overweeg een e-commerce-applicatie die productinformatie weergeeft die is opgehaald van een externe server. Een component, ProductDisplay, is verantwoordelijk voor het weergeven van productdetails. De server kan echter af en toe onverwachte gegevens retourneren, wat kan leiden tot weergavefouten.


// ProductDisplay.js
import React from 'react';

function ProductDisplay({ product }) {
  // Simulate a potential error if product.price is not a number
  if (typeof product.price !== 'number') {
    throw new Error('Ongeldige productprijs');
  }

  return (
    <div>
      <h2>{product.name}</h2>
      <p>Prijs: {product.price}</p>
      <img src={product.imageUrl} alt={product.name} />
    </div>
  );
}

export default ProductDisplay;

Om tegen dergelijke fouten te beschermen, omwikkelt u het component ProductDisplay met een ErrorBoundary:


// App.js
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import ProductDisplay from './ProductDisplay';

function App() {
  const product = {
    name: 'Voorbeeldproduct',
    price: 'Geen nummer', // Opzettelijk onjuiste gegevens
    imageUrl: 'https://example.com/image.jpg'
  };

  return (
    <div>
      <ErrorBoundary>
        <ProductDisplay product={product} />
      </ErrorBoundary>
    </div>
  );
}

export default App;

In dit scenario, omdat de product.price opzettelijk is ingesteld op een tekenreeks in plaats van een getal, veroorzaakt het component ProductDisplay een fout. De ErrorBoundary vangt deze fout op, voorkomt dat de hele applicatie crasht en geeft in plaats daarvan de fallback-UI weer in plaats van het defecte component ProductDisplay.

4. Error Boundaries in geïnternationaliseerde applicaties

Bij het bouwen van applicaties voor een wereldwijd publiek, moeten foutmeldingen worden gelokaliseerd om een betere gebruikerservaring te bieden. Error Boundaries kunnen worden gebruikt in combinatie met internationalisatie (i18n)-bibliotheken om vertaalde foutmeldingen weer te geven.


// ErrorBoundary.js (met i18n-ondersteuning)
import React from 'react';
import { useTranslation } from 'react-i18next'; // Assuming you're using react-i18next

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

  static getDerivedStateFromError(error) {
    return {
      hasError: true,
      error: error,
    };
  }

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
    this.setState({errorInfo: errorInfo});
  }

  render() {
    if (this.state.hasError) {
      return (
        <FallbackUI error={this.state.error} errorInfo={this.state.errorInfo}/>
      );
    }

    return this.props.children;
  }
}

const FallbackUI = ({error, errorInfo}) => {
  const { t } = useTranslation();

  return (
    <div>
      <h2>{t('error.title')}</h2>
      <p>{t('error.message')}</p>
      <details style={{ whiteSpace: 'pre-wrap' }}>
        {error && error.toString()}<br />
        {errorInfo?.componentStack}
      </details>
    </div>
  );
}


export default ErrorBoundary;

In dit voorbeeld gebruiken we react-i18next om de fouttitel en het bericht in de fallback-UI te vertalen. De functies t('error.title') en t('error.message') halen de juiste vertalingen op op basis van de geselecteerde taal van de gebruiker.

5. Overwegingen voor server-side rendering (SSR)

Bij gebruik van Error Boundaries in server-side rendered applicaties is het cruciaal om fouten correct af te handelen om te voorkomen dat de server crasht. De documentatie van React beveelt aan om Error Boundaries niet te gebruiken om te herstellen van weergavefouten op de server. In plaats daarvan moet u fouten afhandelen voordat u de component weergeeft, of een statische foutpagina op de server weergeven.

Best practices voor het gebruik van Error Boundaries

Geavanceerde strategieën voor foutafhandeling

1. Herhaalmechanismen

In sommige gevallen is het mogelijk om te herstellen van een fout door de bewerking die de fout veroorzaakte opnieuw uit te voeren. Als bijvoorbeeld een netwerkaanvraag mislukt, kunt u deze na een korte vertraging opnieuw proberen. Error Boundaries kunnen worden gecombineerd met herhaalmechanismen om een meer veerkrachtige gebruikerservaring te bieden.


// ErrorBoundaryWithRetry.js
import React from 'react';

class ErrorBoundaryWithRetry extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      retryCount: 0,
    };
  }

  static getDerivedStateFromError(error) {
    return {
      hasError: true,
    };
  }

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
  }

  handleRetry = () => {
    this.setState(prevState => ({
      hasError: false,
      retryCount: prevState.retryCount + 1,
    }), () => {
      // This forces the component to re-render.  Consider better patterns with controlled props.
      this.forceUpdate(); // WARNING: Use with caution
      if (this.props.onRetry) {
          this.props.onRetry();
      }
    });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Er is iets fout gegaan.</h2>
          <button onClick={this.handleRetry}>Opnieuw proberen</button>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundaryWithRetry;

Het component ErrorBoundaryWithRetry bevat een knop voor opnieuw proberen die, wanneer erop wordt geklikt, de status hasError reset en de kindcomponenten opnieuw weergeeft. U kunt ook een retryCount toevoegen om het aantal keren te beperken dat opnieuw wordt geprobeerd. Deze aanpak kan vooral nuttig zijn voor het afhandelen van tijdelijke fouten, zoals tijdelijke netwerkstoringen. Zorg ervoor dat de `onRetry`-prop correct wordt afgehandeld en de logica die mogelijk fouten heeft veroorzaakt opnieuw ophaalt/uitvoert.

2. Feature flags

Met feature flags kunt u functies in uw applicatie dynamisch in- of uitschakelen, zonder nieuwe code te implementeren. Error Boundaries kunnen worden gebruikt in combinatie met feature flags om de functionaliteit op gracieuze wijze te degraderen in het geval van een fout. Als bijvoorbeeld een bepaalde functie fouten veroorzaakt, kunt u deze uitschakelen met behulp van een feature flag en een bericht aan de gebruiker weergeven dat aangeeft dat de functie tijdelijk niet beschikbaar is.

3. Circuit breaker-patroon

Het circuit breaker-patroon is een softwareontwerppatroon dat wordt gebruikt om te voorkomen dat een applicatie herhaaldelijk probeert een bewerking uit te voeren die waarschijnlijk mislukt. Het werkt door de succes- en faalfrequenties van een bewerking te bewaken en, als de faalfrequentie een bepaalde drempel overschrijdt, 'het circuit te openen' en verdere pogingen om de bewerking uit te voeren gedurende een bepaalde periode te voorkomen. Dit kan helpen cascadefouten te voorkomen en de algehele stabiliteit van de applicatie te verbeteren.

Error Boundaries kunnen worden gebruikt om het circuit breaker-patroon te implementeren in React-applicaties. Wanneer een Error Boundary een fout opvangt, kan het een foutteller verhogen. Als de foutteller een drempel overschrijdt, kan de Error Boundary een bericht aan de gebruiker weergeven dat aangeeft dat de functie tijdelijk niet beschikbaar is en verdere pogingen om de bewerking uit te voeren voorkomen. Na een bepaalde periode kan de Error Boundary 'het circuit sluiten' en pogingen om de bewerking opnieuw uit te voeren weer toestaan.

Conclusie

React Error Boundaries zijn een essentieel hulpmiddel voor het bouwen van robuuste en gebruiksvriendelijke applicaties. Door Error Boundaries te implementeren, kunt u voorkomen dat fouten uw hele applicatie laten crashen, uw gebruikers een gracieuze fallback-UI bieden en fouten registreren bij bewakingsservices voor het debuggen en analyseren. Door de best practices en geavanceerde strategieën in deze gids te volgen, kunt u React-applicaties bouwen die veerkrachtig en betrouwbaar zijn en een positieve gebruikerservaring bieden, zelfs in het geval van onverwachte fouten. Vergeet niet om u te concentreren op het leveren van behulpzame foutberichten die zijn gelokaliseerd voor een wereldwijd publiek.