Der Lebenszyklus einer React Komponente

Avatar of Kingsley Silas
Kingsley Silas am

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

Eine React-Komponente durchläuft verschiedene Phasen, während sie in einer Anwendung existiert, auch wenn es vielleicht nicht offensichtlich ist, dass im Hintergrund etwas passiert.

Diese Phasen sind

  • Mounting (Einbau)
  • Updating (Aktualisierung)
  • Unmounting (Ausbau)
  • Fehlerbehandlung

Es gibt Methoden in jeder dieser Phasen, die es ermöglichen, während dieser Phase spezifische Aktionen an der Komponente durchzuführen. Wenn Sie beispielsweise Daten aus einem Netzwerk abrufen, möchten Sie die Funktion, die den API-Aufruf behandelt, in der Methode componentDidMount() aufrufen, die während der Mounting-Phase verfügbar ist.

Das Wissen um die verschiedenen Lifecycle-Methoden ist wichtig für die Entwicklung von React-Anwendungen, da es uns ermöglicht, Aktionen genau dann auszulösen, wenn sie benötigt werden, ohne uns mit anderen zu verheddern. Wir werden in diesem Beitrag jede Phase des Lebenszyklus untersuchen, einschließlich der verfügbaren Methoden und der Szenarien, in denen wir sie einsetzen würden.

Die Mounting-Phase

Betrachten Sie das **Mounting** als die Anfangsphase im Lebenszyklus einer Komponente. Bevor das Mounting stattfindet, existiert eine Komponente noch nicht – sie ist lediglich ein Funken in den Augen des DOM, bis das Mounting stattfindet und die Komponente als Teil des Dokuments eingebunden wird.

Es gibt viele Methoden, die wir nutzen können, sobald eine Komponente eingebunden ist: constructor() , render(), componentDidMount() und static getDerivedStateFromProps(). Jede davon ist für sich nützlich, also betrachten wir sie in dieser Reihenfolge.

constructor()

Die Methode constructor() wird erwartet, wenn der State direkt auf einer Komponente gesetzt wird, um Methoden miteinander zu binden. Hier ist, wie sie aussieht

// Once the input component is mounting...
constructor(props) {
  // ...set some props on it...
  super(props);
  // ...which, in this case is a blank username...
  this.state = {
    username: ''
  };
  // ...and then bind with a method that handles a change to the input
  this.handleInputChange = this.handleInputChange.bind(this);
}

Es ist wichtig zu wissen, dass der Konstruktor die erste Methode ist, die aufgerufen wird, wenn die Komponente erstellt wird. Die Komponente hat noch nicht gerendert (das kommt noch), aber der DOM ist sich ihrer bewusst und wir können darauf zugreifen, bevor sie gerendert wird. Daher ist dies nicht der Ort, an dem wir setState() aufrufen oder Nebenwirkungen einführen würden, denn die Komponente befindet sich noch in der Phase der Konstruktion!

Ich habe vor einiger Zeit ein Tutorial über Refs geschrieben, und eine Sache, die ich bemerkt habe, ist, dass es möglich ist, im Konstruktor eine Ref einzurichten, wenn man React.createRef() verwendet. Das ist legitim, da Refs verwendet werden, um Werte ohne Props zu ändern oder die Komponente mit aktualisierten Werten neu rendern zu müssen.

constructor(props) {
  super(props);
  this.state = {
    username: ''
  };
  this.inputText = React.createRef();
}

render()

Die Methode render() ist dort, wo das Markup für die Komponente auf der Benutzeroberfläche sichtbar wird. Benutzer können sie zu diesem Zeitpunkt sehen und darauf zugreifen. Wenn Sie jemals eine React-Komponente erstellt haben, sind Sie bereits damit vertraut – auch wenn Sie es nicht gemerkt haben –, da sie erforderlich ist, um das Markup auszugeben.

class App extends React.Component {
  // When mounting is in progress, please render the following!
  render() {
    return (
      <div>
        <p>Hello World!</p>
      </div>
    )
  }
}

Aber das ist nicht alles, wofür render() gut ist! Sie kann auch verwendet werden, um eine Liste von Komponenten zu rendern

class App extends React.Component {
  render () {
    return [
      <h2>JavaScript Tools</h2>,
      <Frontend />,
      <Backend />
    ]
  }
}

…und sogar Fragmente einer Komponente

class App extends React.Component {
  render() {
    return (
      <React.Fragment>
        <p>Hello World!</p>
      </React.Fragment>
    )
  }
}

Wir können sie auch verwenden, um Komponenten außerhalb der DOM-Hierarchie zu rendern (wie bei React Portals)

// We're creating a portal that allows the component to travel around the DOM
class Portal extends React.Component {
  // First, we're creating a div element
  constructor() {
    super();
    this.el = document.createElement("div");
  }
  
  // Once it mounts, let's append the component's children
  componentDidMount = () => {
    portalRoot.appendChild(this.el);
  };
  
  // If the component is removed from the DOM, then we'll remove the children, too
  componentWillUnmount = () => {
    portalRoot.removeChild(this.el);
  };
  
  // Ah, now we can render the component and its children where we want
  render() {
    const { children } = this.props;
    return ReactDOM.createPortal(children, this.el);
  }
}

Und natürlich kann render() – ähäm – Zahlen und Zeichenketten rendern…

class App extends React.Component {
  render () {
    return "Hello World!"
  }
}

…sowie null oder boolesche Werte

class App extends React.Component {
  render () {
    return null
  }
}

componentDidMount()

Lässt der Name componentDidMount() erahnen, was er bedeutet? Diese Methode wird aufgerufen, nachdem die Komponente eingebunden wurde (d. h. an den DOM angehängt wurde). In einem anderen Tutorial, das ich über Datenabruf in React geschrieben habe, ist dies der Ort, an dem Sie eine Anfrage stellen möchten, um Daten von einer API zu erhalten.

Wir können Ihre Fetch-Methode haben

fetchUsers() {
  fetch(`https://jsonplaceholder.typicode.com/users`)
    .then(response => response.json())
    .then(data =>
      this.setState({
        users: data,
        isLoading: false,
      })
    )
  .catch(error => this.setState({ error, isLoading: false }));
}

Dann rufen Sie die Methode im componentDidMount() Hook auf

componentDidMount() {
  this.fetchUsers();
}

Wir können auch Event-Listener hinzufügen

componentDidMount() {
  el.addEventListener()
}

Nett, oder?

static getDerivedStateFromProps()

Es ist ein etwas langatmiger Name, aber static getDerivedStateFromProps() ist nicht so kompliziert, wie es klingt. Sie wird vor der Methode render() während der Mounting-Phase und vor der Update-Phase aufgerufen. Sie gibt entweder ein Objekt zurück, um den State einer Komponente zu aktualisieren, oder null, wenn nichts zu aktualisieren ist.

Um zu verstehen, wie es funktioniert, implementieren wir eine Zählerkomponente, die einen bestimmten Wert für ihren counter-State hat. Dieser State wird nur aktualisiert, wenn der Wert von maxCount höher ist. maxCount wird von der Elternkomponente übergeben.

Hier ist die Elternkomponente

class App extends React.Component {
  constructor(props) {
    super(props)
    
    this.textInput = React.createRef();
    this.state = {
      value: 0
    }
  }
  
  handleIncrement = e => {
    e.preventDefault();
    this.setState({ value: this.state.value + 1 })
  };

  handleDecrement = e => {
    e.preventDefault();
    this.setState({ value: this.state.value - 1 })
  };

  render() {
    return (
      <React.Fragment>
        <section className="section">
          <p>Max count: { this.state.value }</p>
          <button
            onClick={this.handleIncrement}
            class="button is-grey">+</button>
          <button
            onClick={this.handleDecrement}
            class="button is-dark">-</button>          
        </section>
        <section className="section">
          <Counter maxCount={this.state.value} />
        </section>
      </React.Fragment>
    )
  }
}

Wir haben eine Schaltfläche, mit der der Wert von maxCount erhöht wird, den wir an die Counter-Komponente übergeben.

class Counter extends React.Component {
  state={
    counter: 5
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (prevState.counter < nextProps.maxCount) {
      return {
        counter: nextProps.maxCount
      };
    } 
    return null;
  }

  render() {
    return (
      <div className="box">
        <p>Count: {this.state.counter}</p>
      </div>
    )
  }
}

In der Counter-Komponente prüfen wir, ob counter kleiner als maxCount ist. Wenn ja, setzen wir counter auf den Wert von maxCount. Andernfalls tun wir nichts.

Sie können mit dem folgenden Pen herumspielen, um zu sehen, wie das auf der Benutzeroberfläche funktioniert

Siehe den Pen
getDerivedStateFromProps
von Kingsley Silas Chijioke (@kinsomicrote)
auf CodePen.


Die Updating-Phase

Die **Updating**-Phase tritt ein, wenn sich die props oder der state einer Komponente ändern. Wie das Mounting hat auch das Updating eigene Methoden, die wir uns als Nächstes ansehen werden. Es ist jedoch erwähnenswert, dass sowohl render() als auch getDerivedStateFromProps() auch in dieser Phase ausgelöst werden.

shouldComponentUpdate()

Wenn sich der state oder die props einer Komponente ändern, können wir die Methode shouldComponentUpdate() verwenden, um zu steuern, ob die Komponente aktualisiert werden soll oder nicht. Diese Methode wird vor dem Rendern aufgerufen und wenn State und Props empfangen werden. Das Standardverhalten ist true. Um bei jeder Änderung des States oder der Props neu zu rendern, würden wir etwas wie folgt tun

shouldComponentUpdate(nextProps, nextState) {
  return this.state.value !== nextState.value;
}

Wenn false zurückgegeben wird, aktualisiert sich die Komponente nicht, und stattdessen wird die Methode render() aufgerufen, um die Komponente anzuzeigen.

getSnapshotBeforeUpdate()

Eine Sache, die wir tun können, ist, den Zustand einer Komponente zu einem bestimmten Zeitpunkt zu erfassen, und dafür ist getSnapshotBeforeUpdate() gedacht. Sie wird nach render(), aber vor der Übernahme neuer Änderungen in den DOM aufgerufen. Der zurückgegebene Wert wird als dritter Parameter an componentDidUpdate() übergeben.

Sie nimmt den vorherigen State und Props als Parameter entgegen

getSnapshotBeforeUpdate(prevProps, prevState) {
  // ...
}

Anwendungsfälle für diese Methode sind eher selten, zumindest nach meiner Erfahrung. Dies ist eine dieser Lifecycle-Methoden, die Sie wahrscheinlich nicht sehr oft verwenden werden.

componentDidUpdate()

Fügen Sie componentDidUpdate() zur Liste der Methoden hinzu, bei denen der Name alles sagt. Wenn sich die Komponente aktualisiert, können wir sie mit dieser Methode an diesem Zeitpunkt nutzen und ihr die vorherigen props und den state der Komponente übergeben.

componentDidUpdate(prevProps, prevState) {
  if (prevState.counter !== this.state.counter) {
    // ...
  }
}

Wenn Sie getSnapshotBeforeUpdate() jemals verwenden, können Sie den zurückgegebenen Wert auch als Parameter an componentDidUpdate() übergeben.

componentDidUpdate(prevProps, prevState, snapshot) {
  if (prevState.counter !== this.state.counter) {
    // ....
  }
}

Die Unmounting-Phase

Hier betrachten wir ziemlich genau das Gegenteil der Mounting-Phase. Wie Sie vielleicht erwarten, tritt das **Unmounting** ein, wenn eine Komponente aus dem DOM entfernt und nicht mehr verfügbar ist.

Wir haben hier nur eine Methode: componentWillUnmount()

Diese wird aufgerufen, bevor eine Komponente aus dem DOM entfernt und zerstört wird. Hier würden wir alle notwendigen Bereinigungen durchführen, nachdem die Komponente verschwunden ist, wie z. B. das Entfernen von Event-Listenern, die möglicherweise in componentDidMount() hinzugefügt wurden, oder das Leeren von Abonnements.

// Remove event listener 
componentWillUnmount() {
  el.removeEventListener()
}

Die Fehlerbehandlungsphase

In einer Komponente können Dinge schiefgehen, was zu Fehlern führen kann. Wir haben schon seit einiger Zeit Fehlergrenzen, um dabei zu helfen. Diese Fehlergrenzen-Komponente nutzt einige Methoden, um uns bei der Behandlung von Fehlern zu unterstützen, auf die wir stoßen könnten.

getDerivedStateFromError()

Wir verwenden getDerivedStateFromError(), um Fehler abzufangen, die von einer absteigenden Komponente ausgelöst werden, und verwenden diese dann, um den State der Komponente zu aktualisieren.

class ErrorBoundary extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <h1>Oops, something went wrong :(</h1>
      );
    }

    return this.props.children;
  }
}

In diesem Beispiel zeigt die ErrorBoundary-Komponente "Oops, etwas ist schief gelaufen" an, wenn ein Fehler von einer Kindkomponente ausgelöst wird. Wir haben weitere Informationen zu dieser Methode in einem Rundgang durch die Goodies, die in React 16.6.0 veröffentlicht wurden.

componentDidCatch()

Während getDerivedStateFromError() geeignet ist, den State der Komponente in Fällen zu aktualisieren, in denen Nebenwirkungen wie die Fehlerprotokollierung stattfinden, sollten wir componentDidCatch() verwenden, da sie während der Commit-Phase aufgerufen wird, wenn der DOM aktualisiert wurde.

componentDidCatch(error, info) {
  // Log error to service
}

Sowohl getDerivedStateFromError() als auch componentDidCatch() können in der ErrorBoundary-Komponente verwendet werden.

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

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

  componentDidCatch(error, info) {
    // Log error to service
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <h1>Oops, something went wrong :(</h1>
      );
    }

    return this.props.children;
  }
}

Und das ist der Lebenszyklus einer React-Komponente!

Es ist faszinierend zu wissen, wie eine React-Komponente mit dem DOM interagiert. Man könnte leicht denken, dass "Magie" passiert und dann etwas auf einer Seite erscheint. Aber der Lebenszyklus einer React-Komponente zeigt, dass es eine Ordnung im Chaos gibt und dass er uns eine Menge Kontrolle gibt, um Dinge vom Zeitpunkt des Einbaus der Komponente in den DOM bis zu ihrem Verschwinden zu steuern.

Wir haben viel Stoff in relativ kurzer Zeit behandelt, aber hoffentlich gibt Ihnen dies einen guten Einblick, wie React Komponenten handhabt und welche Fähigkeiten wir in verschiedenen Phasen dieser Handhabung haben. Hinterlassen Sie gerne Fragen, wenn etwas unklar geblieben ist, und ich werde mein Bestes tun, um Ihnen zu helfen!