Fehlerbehandlung mit Fehler Grenze

Avatar of Kingsley Silas
Kingsley Silas am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Denken und Aufbauen in React beinhaltet das Herangehen an Anwendungsdesign in Blöcken oder Komponenten. Jeder Teil Ihrer Anwendung, der eine Aktion ausführt, kann und sollte als Komponente behandelt werden. Tatsächlich ist React komponentenbasiert und, wie Tomas Eglinkas kürzlich schrieb, sollten wir dieses Konzept nutzen und lieber dazu neigen, große Blöcke in kleinere Komponenten aufzuteilen.

Das Aufteilen führt unweigerlich zu Komponentenhierarchien, was gut ist, weil sie aufgeblähte Komponenten und Architektur vermeiden. Die Dinge können jedoch kompliziert werden, wenn in einer Kindkomponente ein Fehler auftritt. Was passiert, wenn die gesamte Anwendung abstürzt?! Ernsthaft, React, warum müssen die Eltern- und Geschwisterkomponenten für die Sünden einer anderen Komponente büßen? Warum?

Fehlergrenzen

React 16 brachte viele Goodies mit sich, darunter Fehlergrenzen. Lassen Sie uns die Dokumentation konsultieren und aufschlüsseln, was sie über dieses Juwel sagt, denn wir können es verwenden, um Fehler dort zu erkennen, wo sie auftreten, und sie schneller und mit weniger Kopfschmerzen beheben!

Fehlergrenzen sind React-Komponenten, die JavaScript-Fehler irgendwo in ihrem Kindkomponentenbaum abfangen, diese Fehler protokollieren und eine Fallback-Benutzeroberfläche anstelle des abgestürzten Komponententurms anzeigen. Fehlergrenzen fangen Fehler während des Renderns, in Lebenszyklusmethoden und in Konstruktoren des gesamten Baums unter ihnen ab.

Das ist viel Fachjargon, aber wie Komponenten können wir es in weniger komplexe Blöcke zerlegen.

Fehlergrenzen sind React-Komponenten

Das macht Sinn und ist nützlich, da es ein Konzept ist, das wir schon immer verwendet haben. Der Unterschied ist, dass Saft darauf gesprüht wurde, um es von einer normalen Komponente zu unterscheiden. Dennoch vergessen Sie nicht die grundlegende Idee, dass Fehlergrenzen selbst React-Komponenten sind!

Fehlergrenzen fangen JavaScript-Fehler irgendwo in ihrem Kindkomponentenbaum ab

Falls Sie vergessen haben, wie Kindkomponentenbäume funktionieren, hier ist ein Beispiel

<ParentComponent>
  <FirstChild>
    <FirstChildDaughter>
    </FirstChildDaughter>
  </FirstChild>
  <SecondChild>
  </SecondChild>
</ParentComponent>

Wir haben zwei Eltern- und drei Kindkomponenten. Gemäß dem, was wir bisher über Fehlergrenzen gelernt haben, können wir den obigen Baum nachbilden zu

<ErrorBoundaryComponent>
  <ParentComponent>
    <FirstChild>
      <FirstChildDaughter>
      </FirstChildDaughter>
    </FirstChild>
    <SecondChild>
    </SecondChild>
  </ParentComponent>
</ErrorBoundaryComponent>

Durch das Umwickeln des gesamten Baumes mit einer ErrorBoundaryComponent können wir alle JavaScript-Fehler abfangen, die in ihren Kindkomponenten auftreten. Cool, oder?

Fehlergrenzen protokollieren diese Fehler

Wenn Fehler abgefangen werden, möchten wir, dass die Fehlergrenzen etwas damit tun, vorzugsweise etwas, das uns darüber informiert. Entwickler nutzen oft Fehlerprotokollierungsplattformen, um Fehler zu überwachen, die in ihrer Software auftreten. Mit Fehlergrenzen können wir dasselbe tun.

Fehlergrenzen zeigen eine Fallback-Benutzeroberfläche an

Anstatt die ganze ärgerliche Kombination von Rottönen in verschiedenen Schattierungen anzuzeigen, können Sie eine angepasste Benutzeroberfläche wählen, die angezeigt wird, wenn ein Fehler auftritt. Das kann sehr nützlich sein, da es Ihnen ermöglicht, Fehler in einer Weise zu gestalten, die für Sie leichter zu lesen und zu scannen ist. Super cool, oder?

Wie ich werden Sie denken, dass dies bedeutet, dass Fehlergrenzen alle JavaScript-Fehler abfangen. Leider stimmt das nicht. Hier sind Fehler, die sie gnädig ignorieren werden

  • Ereignis-Handler
  • Asynchrone Codes (z.B. setTimeout oder requestAnimationFrame Callbacks)
  • Server-seitiges Rendering
  • Fehler, die in der Fehlergrenze selbst ausgelöst werden (anstelle ihrer Kinder)

componentDidCatch()

Der zusätzliche Saft, der eine Komponente zu einer Fehlergrenze macht, ist componentDidCatch() — dies ist eine Lebenszyklusmethode, die wie der JavaScript-catch{}-Block funktioniert, aber für Komponenten. Wenn ein Fehler in einer Kindkomponente gefunden wird, wird der Fehler von der nächstgelegenen Fehlergrenze behandelt. Nur Klassenkomponenten können Fehlergrenzen sein.

componentDidCatch() akzeptiert zwei Parameter

  • error: Dies ist der ausgelöste Fehler
  • info: Ein Objekt, das eine Spur enthält, wo der Fehler aufgetreten ist

Fehlergrenze in Aktion

Nehmen wir an, wir arbeiten an einer Funktion, die Standorte auflistet, an denen Konferenzen abgehalten werden können. Etwas wie das

Sehen Sie den Pen error boundary 0 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen.

Die Anwendung listet Standorte aus der Location-Komponente auf, und die einzelnen Standorte werden als Location Cards ausgegeben. Wir achten besonders darauf, dass der Name jedes Standorts zur Konsistenz in Großbuchstaben gerendert wird. Für dieses Tutorial werden wir der Liste der Standorte ein leeres Objekt hinzufügen.

class Location extends React.Component {
  state = {
    locations: [
      {
        "name": "Ojo",
        "zone": "Lagos State",
        "region": "South West"
      },
      {
        "name": "Ahiazu Mbaise",
        "zone": "Imo State",
        "region": "South East"
      },
      {
        "name": "Akoko-Edo",
        "zone": "Edo State",
        "region": "South South"
      },
      {
        "name": "Anka",
        "zone": "Zamfara State",
        "region": "North West"
      },
      {
        "name": "Akwanga",
        "zone": "Nasarawa State",
        "region": "North Central"
      },
      {
        
      }
    ]
  }
  render() {
    return (
      <div>
        <div>
          <div>
            <h2>Locations</h2>
          </div>
        </div>
        <div>
          {this.state.locations
            .map(location => 
              <LocationCard key={location.id} {...location} />
          )}
        </div>
      </div>
    )
  }
}

const LocationCard = (props) => {
  return (
    <div>
      <hr />
      <p><b>Name:</b> {props.name.toUpperCase()}</p>
      <p><b>Zone:</b> {props.zone}</p>
      <p><b>Region:</b> {props.region}</p>
      <hr />
    </div>
  )
}

const App = () => (
  <div>
     <Location />
  </div>
)

ReactDOM.render(<App />, document.getElementById("root"));

Wenn Sie dies im Browser ausführen, sehen Sie einen Fehler ähnlich diesem Screenshot

A screenshot of the Type Error providing the error message Cannot read property toUpperCase of undefined. Background color is tan and there is a block of code with a light red background indicating where the error is in the code base.

Das ist nicht sehr hilfreich, also wenden wir eine Fehlergrenze an, um uns zu helfen. Zuerst erstellen wir eine ErrorBoundary-Komponente

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      info: null
    };
  }
  componentDidCatch(error, info) {
    this.setState({
      hasError: true,
      error: error,
      info: info
    });
  }
  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h1>Oops, something went wrong :(</h1>
          <p>The error: {this.state.error.toString()}</p>
          <p>Where it occured: {this.state.info.componentStack}</p>
        </div>
      );
    }
    return this.props.children;
  }
}

Ein anfänglicher Zustand für hasError, error und info wird erstellt. Dann wird die Lebenszyklusmethode componentDidCatch() hinzugefügt. Wenn in einem Konstruktor, Render- oder Lebenszyklusmethode einer seiner Kindkomponenten ein Fehler auftritt, wird der Zustand hasError auf true geändert. Wenn dies geschieht, rendert die ErrorBoundary-Komponente und zeigt den Fehler an. Wenn jedoch keine Fehler auftreten, werden die Kinder der ErrorBoundary-Komponente wie erwartet gerendert.

Als Nächstes müssen wir sowohl die ErrorBoundary- als auch die Location-Komponente zu unserer Haupt-App-Komponente hinzufügen

const App = () => (
  <div>
    <ErrorBoundary>
      <Location />
    </ErrorBoundary>
  </div>
)

Sehen Sie den Pen error boundary 2 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen.

Wir sehen die nervige TypeError-Benutzeroberfläche nicht mehr! Es funktioniert!

Es gibt noch eine kleine Sache, die wir zur Verbesserung der App tun können. Wenn Sie sich den Code in der Demo ansehen, sehen Sie ein leeres Objekt, das wir am Ende hinzugefügt haben. Ist es möglich, dass die anderen gültigen Standorte gerendert werden? Absolut! Innerhalb der Location-Komponente können wir die LocationCard-Komponente mit der ErrorBoundary-Komponente umschließen, um die Fehlerprotokollierung direkt auf die Karten zu beschränken

class Location extends React.Component {
  state = {
    locations: [
      {
        "name": "Ojo",
        "zone": "Lagos State",
        "region": "South West"
      },
      {
        "name": "Ahiazu Mbaise",
        "zone": "Imo State",
        "region": "South East"
      },
      {
        "name": "Akoko-Edo",
        "zone": "Edo State",
        "region": "South South"
      },
      {
        "name": "Anka",
        "zone": "Zamfara State",
        "region": "North West"
      },
      {
        "name": "Akwanga",
        "zone": "Nasarawa State",
        "region": "North Central"
      },
      {
        // Empty!
      }
    ]
  }
  render() {
    return (
      <div>
        <div>
          <div>
            <h2>Locations</h2>
          </div>
        </div>
        <div>
          {this.state.locations
            .map(location => 
            <ErrorBoundary>
              // Should render all locations, but the empty instance
              <LocationCard key={location.id} {...location} />
            </ErrorBoundary>
          )}
        </div>
      </div>
    )
  }
}

Diesmal werden die gültigen Standorte angezeigt, mit Ausnahme des leeren. Sie können wählen, ob Sie den gesamten Komponententurmpaket mit einer Fehlergrenzenkomponente einmal umschließen oder ob Sie verschiedene Komponenten an strategischen Stellen umschließen möchten. Die Entscheidung liegt bei Ihnen.

Zusammenfassend

Ich ermutige Sie, Fehlergrenzen in Ihren Anwendungen zu nutzen. Ebenso lohnt es sich, etwas tiefer einzutauchen. Dazu finden Sie hier einige Probleme im React-Repo zu Fehlergrenzen und Ereignis-Handlern. Gehen Sie diese durch, damit Sie den aktuellen Stand der Dinge sehen können