Klassen- vs Funktionskomponenten

React Hooks: Wie man von Klassenkomponenten auf Hooks migriert
Keine Kommentare

React Hooks wurden in React als neues Feature veröffentlich, um State und Seiteneffekte in React-Funktionskomponenten verfügbar zu machen. Zuvor konnten sie nur in React-Klassenkomponenten implementiert werden. Aber da sich Reacts Art der Implementierung von Komponenten im Laufe der Jahre gewandelt hat, stehen die Features von Klassenkomponenten durch React Hooks nun auch in Funktionskomponenten zur Verfügung.

Dieses Tutorial zeigt einen Migrationspfad auf, anhand dessen man Klassenkomponenten durch React Hooks auch als Funktionskomponenten schreiben kann. Daher betrachten wir nun anhand einiger Beispiele, wie man sowohl die State-Verwaltung als auch Seiteneffekte, die in einer Klassenkomponente verwendet werden, migriert, um sie in einer Funktionskomponente zu nutzen.

Ich würde nicht empfehlen, alle React-Klassenkomponenten mit Hooks zu Funktionskomponenten umzuschreiben. Vielmehr soll das Tutorial einen Weg aufzeigen, wie eine solche Implementierung gelingt, ohne auf Features zu verzichten. Auch stellt es einen Leitfaden für die eigene Entscheidung für oder gegen Funktionskomponenten mit Hooks in React dar.

Komponenten-State mit Reacts useState Hook

React-Klassenkomponenten waren bisher die Allzweckwaffe zur Implementierung von stateful Komponenten. Es ist möglich, den initialen State in einem Konstruktor zuzuweisen, ihn mit der gegebenen this.setState()-Methode zu schreiben – was oft in ausgelagerten Klassenmethoden gemacht wurde – und ihn mit this.state aus der Komponenteninstanz zu lesen (Listing 1).

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      value: '',
    };
  }

  onChange = event => {
    this.setState({ value: event.target.value });
  };

  render() {
    return (
      <div>
        <h1>Hello React ES6 Class Component!</h1>

        <input
          value={this.state.value}
          type="text"
          onChange={this.onChange}
        />

        <p>{this.state.value}</p>
      </div>
    );
  }
}

Eine Funktionskomponente kann jetzt durch einen React Hook namens useState das Gleiche bewirken. Der Hook lässt uns den initialen State (z. B. einen leeren String) zuweisen und gibt ein Array zurück, das den State und eine Funktion zum Updaten des States beinhaltet. Durch die Verwendung des JavaScript Array Destructuring können wir ganz bequem mit einer Codezeile die ausgegebenen Werte aus dem Array extrahieren (Listing 2)

const App = () => {
  const [value, setValue] = React.useState('');

  const onChange = event => setValue(event.target.value);

  return (
    <div>
      <h1>Hello React Function Component!</h1>

      <input value={value} type="text" onChange={onChange} />

      <p>{value}</p>
    </div>
  );
};

Von Natur aus sind React-Funktionskomponenten viel leichtgewichtiger als Klassenkomponenten. Man braucht sich nicht mehr mit einem Konstruktor oder Klassenmethoden abmühen, weil der React Hook für die State-Verwaltung eingesetzt werden kann, während weitere Funktionen direkt in der Funktionskomponente definiert werden können (z. B. onChange()).

Wer also eine Komponente in React implementieren will, die einen State verwalten muss, muss nicht zur Klassenkomponente greifen, sondern kann dem Ganzen mit Hooks eine Chance geben.

Komponenten-Seiteneffekte mit Reacts useEffect Hook

Lasst uns jetzt das obige Beispiel weiterentwickeln, um einen Seiteneffekt zu verwenden. Zuerst werden wir diesen in der React-Klassenkomponente einführen und dann schauen wir uns an, wie er durch Hooks in einer Funktionskomponente implementiert werden kann. In unserem Fall geht es um die Verwendung des lokalen Speichers des Browsers in unserer Komponente, was in Listing 3 zu sehen ist.

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      value: localStorage.getItem('myValueInLocalStorage') || '',
    };
  }

  componentDidUpdate() {
    localStorage.setItem('myValueInLocalStorage', this.state.value);
  }

  onChange = event => {
    this.setState({ value: event.target.value });
  };

  render() {
    return (
      <div>
        <h1>Hello React ES6 Class Component!</h1>

        <input
          value={this.state.value}
          type="text"
          onChange={this.onChange}
        />

        <p>{this.state.value}</p>
      </div>
    );
  }
}

Nun wird mit jedem Update der Komponente (z. B. wenn sich der State ändert), der Wert des States – ursprünglich vom geänderten Wert im Eingabefeld stammend – im lokalen Speicher des Browsers abgelegt. Wenn die Anwendung durch ein Aktualisieren des Browsers neu gestartet wird, wird durch den Konstruktor der Komponente sichergestellt, dass der initiale State aus dem lokalen Speicher verwendet wird. Ein Fallback stellt sicher das der Wert mit einem leeren String initialisiert wird.

iJS React Cheat Sheet

Free: React Cheat Sheet

You want to improve your knowledge in React or just need some kind of memory aid? We have the right thing for you: the iJS React Cheat Sheet (written by Joel Lord). Now you will always know how to React!

Da diese Komponente den lokalen Speicher verwendet, ist deren Output der Render-Methode nicht vorhersehbar, wenn nur die Props der Komponente bekannt sind. Schließlich ist ein Seiteneffekt daran beteiligt, Informationen von irgendwo außerhalb des Inputs (Props) der Komponente zu beziehen.

Jetzt schauen wir uns einmal an, wie das identische Feature – Synchronisierung des Wertes aus dem Eingabefeld mit dem lokalen Speicher – mit einer Funktionskomponente unter Verwendung von Reacts useEffect Hook implementiert werden kann (Listing 4)

const App = () => {
  const [value, setValue] = React.useState(
    localStorage.getItem('myValueInLocalStorage') || '',
  );
  React.useEffect(() => {
    localStorage.setItem('myValueInLocalStorage', value);
  }, [value]);
  const onChange = event => setValue(event.target.value);
  return (
    <div>
      <h1>Hello React Function Component!</h1>
      <input value={value} type="text" onChange={onChange} />
      <p>{value}</p>
    </div>
  );
};

Reacts useEffect Hook wird jedes Mal ausgeführt, wenn einer der Werte im weiter gegebenen Array (zweites Argument) verändert wurde. Wenn sich also der Wert des Eingabefeldes ändert, aktualisieren wir damit in unserem Fall jedes Mal den lokalen Speicher. Auch wird der Wert des lokalen Speichers initial verwendet, um den Wert für das Eingabefeld festzulegen.

Wie schon gesagt, ist die Funktionskomponente von Natur aus viel leichtgewichtiger, weil sie State und Seiteneffekte innerhalb ihres Funktionskerns verwenden kann. Auch wird die Nutzung des lokalen Speichers näher ins Zentrum der Funktion gerückt, anstatt ihn wie zuvor auf unterschiedliche Klassenmethoden zu verteilen.

Auch für Komponenten mit Seiteneffekten lohnt sich also ein Blick auf die React Hooks.

Abstraktion mit benutzerdefinierten React Hooks

Alle React Hooks, die wir bisher gesehen haben, sind durch das React-Team ins Framework integriert worden. Allerdings ist es auch möglich, die vorhandenen Hooks zu neuen benutzerdefinierten React Hooks zu kombinieren, um bestimmte Probleme zu lösen. Dadurch eignen sich React Hooks perfekt für wiederverwendbare Komponentenlogik. In unserem Fall können wir die gesamte Logik für den State und den Seiteneffekt mit dem lokalen Speicher zu einem benutzerdefinierten Hook extrahieren, wie in Listing 5 gezeigt wird.

const useStateWithLocalStorage = localStorageKey => {
  const [value, setValue] = React.useState(
    localStorage.getItem(localStorageKey) || '',
  );
  React.useEffect(() => {
    localStorage.setItem(localStorageKey, value);
  }, [value]);
  return [value, setValue];
};
const App = () => {
  const [value, setValue] = useStateWithLocalStorage(
    'myValueInLocalStorage',
  );
  const onChange = event => setValue(event.target.value);
  return (
    <div>
      <h1>Hello React Function Component!</h1>
      <input value={value} type="text" onChange={onChange} />
      <p>{value}</p>
    </div>
  );
};

Der Hook useStateWithLocalStorage ermöglicht uns nicht nur, State-Verwaltung zu nutzen, sondern auch den State mit dem lokalen Browserspeicher zu synchronisieren. Jedes Mal, wenn die Komponente initialisiert, wird der State aus dem lokalen Speicher verwendet, wenn im lokalen Speicher von vornherein ein Wert abgelegt ist.

Benutzerdefinierte Hooks verbinden wiederverwendbare Logik perfekt innerhalb einer Funktion. Während die gesamte Logik in der obigen React-Klassenkomponente ungeordnet umherfliegt, lesen React Hooks die Einzelteile auf und kapseln sie ein. Es wäre möglich gewesen, die gleiche Abstraktionsebene durch eine Higher-Order-Komponente hinzuzufügen – wie hier veranschaulicht –, aber die Logik liegt auch in der Higher-Order-Komponenten ungeordnet vor.

Die meisten der gezeigten Beispiele können hier betrachtet werden. Ich kann nur wärmstens empfehlen, zuerst die unterschiedlichen React-Komponententypen des Projekts durchzugehen, um ein besseres Verständnis dafür zu entwickeln, wie sich React aus historischer Perspektive weiterentwickelt hat.

Für alle, die nun auch mal mit Hooks arbeiten wollen, stellt React alle Tools bereit, die man dafür benötigt. Falls ihr weitere Tutorials zu React Hooks durchgehen wollte, kann ich die folgenden sehr empfehlen:

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -