• Zuhause
  • Artikel
  • So konvertieren Sie React-Klassenkomponenten mithilfe von Hooks in Funktionskomponenten
Veröffentlicht am 09-03-2019

So konvertieren Sie React-Klassenkomponenten mithilfe von Hooks in Funktionskomponenten

Im letzten Monat habe ich viel Zeit mit React-Haken gearbeitet. Ich habe Funktionskomponenten mithilfe von Hooks anstelle der herkömmlichen klassenbasierten Komponenten erstellt. Ich behaupte nicht, dass Hooks definitiv klassenbasierten Komponenten überlegen sind. Es gibt jedoch eine enorme Verbesserung der Benutzerfreundlichkeit, während Ihre Komponenten als Funktionskomponenten geschrieben werden, zumal diese Funktionskomponenten jetzt mithilfe von React-Hooks auf Status und Lebenszyklus zugreifen können.

In diesem Artikel werde ich Ihnen zeigen, wie Sie eine klassenbasierte React-Komponente in eine Funktionskomponente konvertieren und stattdessen die klassenbasierten setState- und Lifecycle-Methoden, wie z. B. ComponentWillMount und ComponentWillReceiveProps, durch React-Hooks ersetzen.

Lassen Sie uns zunächst eine klassenbasierte React-Komponente erstellen, die Status- und Lebenszyklusmethoden verwendet Dafür bauen wir natürlich die traditionelle React Todo-Anwendung auf.

Unsere Todo-App wird so aussehen:

  • Eine Texteingabe, in die Sie neue Aufgaben eingeben können.
    • Eine Schaltfläche "Todo hinzufügen", mit der der neue ToDo-Eintrag in der Texteingabe der ToDo-Liste hinzugefügt wird.
    • Eine Liste mit allen einzelnen Aufgaben.
    • Jedem einzelnen ToDo-Artikel ist ein Kontrollkästchen zugeordnet, mit dem der Artikel als erledigt markiert werden kann.
    • Die ToDos bleiben im lokalen Speicher erhalten und werden beim Starten der App erneut aus dem lokalen Speicher geladen.
    • Unsere Komponenten verwenden die Lebenszyklusmethoden state, componentDidMount, componentDidUpdate und getDerivedStateFromProps. Einige dieser Methoden (ich betrachte Sie mit getDerivedStateFromProps) werden auf äußerst ausgefeilte Weise verwendet, um zu zeigen, wie ein Hook-basierter Ersatz aussehen würde.

      Erstellen der klassenbasierten Todo-App

      Unsere ToDo-App wird wie folgt in drei verschiedenen Komponenten implementiert:

      Den vollständigen Code für diese Beispiele finden Sie auf Github. Verwenden Sie die Tags, um zwischen Klassenkomponenten und Funktionskomponenten zu navigieren.

      Sehen wir uns zuerst die TodoContainer-Komponente an. Diese Komponente behält den vollständigen Status der Anwendung jederzeit in ihrem Status bei. Es gibt zwei Methoden, addTodoItem und toggleItemCompleted, die als Rückrufe an die TodoInput- bzw. TodoItem-Komponenten übergeben werden und zum Hinzufügen eines neuen ToDo-Elements sowie zum Markieren eines Elements als abgeschlossen oder unvollständig verwendet werden.

      Darüber hinaus verwendet die TodoContainer-Komponente die Lifecycle-Methode componentDidMount, um gespeicherte ToDo-Elemente aus dem localStorage zu laden. Wenn keine Aufgaben vorhanden sind, wird eine leere Liste als Status der TodoContainer-Komponente instanziiert. TodoContainer verwendet auch die Lifecycle-Methode componentDidUpdate, um den Status der TodoContainer-Komponente in localStorage zu speichern. Auf diese Weise wird der Status von TodoContainer bei einer Änderung des Status von localStorage beibehalten und kann beim Neustart der App wiederhergestellt werden.

      Alles in allem sieht die TodoContainer-Komponente folgendermaßen aus:

      Als nächstes wollen wir uns die TodoInput-Komponente ansehen. Dies ist eine sehr einfache Komponente, die aus einer einzelnen Texteingabe und einer Schaltfläche besteht. Die Komponente verwendet ihren eigenen Status, um den Wert der Texteingabe zu verfolgen, und gibt den Text beim Klicken auf die Schaltfläche an die addTodoItem-Methode weiter, die von der TodoContainer-Komponente als onAdd prop übergeben wird.

      Die TodoInput-Komponente sieht folgendermaßen aus:

      Zum Schluss die TodoItem-Komponente. Dies ist eine weitere einfache Komponente, die im Wesentlichen aus einem Ankreuzfeld besteht, das angibt, ob das ToDo-Element abgeschlossen ist, und einer Bezeichnung für dieses Ankreuzfeld, die den Text des ToDo-Elements angibt. Um die Verwendung von getDerivedStateFromProps zu demonstrieren, übernimmt die TodoItem-Komponente die vollständigen completeItemIds der TodoContainer-Komponente als Requisite und berechnet daraus, ob dieses TodoItem vollständig ist oder nicht.

      Die TodoItem-Komponente sieht folgendermaßen aus:

      Regeln, die beim Süchtigen zu beachten sind

      Um die verschiedenen React-Hooks zu demonstrieren, die wir zur Konvertierung unserer Todo-App für die Verwendung der React-Funktionskomponenten verwenden werden, beginne ich mit den einfachen Hooks und gehe zu den komplizierteren. Aber bevor wir das tun, werfen wir einen Blick auf die wichtigsten Regeln, die bei der Verwendung von Hooks zu beachten sind.

      • Hooks sollten nur aus einer React-Funktionskomponente oder einem anderen Hook aufgerufen werden.
      • Die gleichen Hooks sollten während des Renderns einer einzelnen Komponente in derselben Reihenfolge und in derselben Anzahl aufgerufen werden. Das bedeutet, dass Hooks nicht innerhalb von Schleifen- oder Bedingungsblöcken aufgerufen werden können, sondern immer auf der obersten Ebene der Funktion aufgerufen werden müssen.

      Der Grund für diese Regeln für die Verwendung von Hooks ist ein Thema, das ein Artikel für sich sein kann. Wenn Sie mehr darüber erfahren möchten, lesen Sie den Artikel über die Regeln der Hooks auf der offiziellen React-Dokumentationsseite.

      Nachdem wir nun die Grundregeln kennen, die bei der Verwendung von Hooks zu beachten sind, beginnen wir mit der Konvertierung unserer Todo-App in Funktionskomponenten.

      Funktionskomponenten für unsere Todo App

      Der einfachste und wahrscheinlich der einzige Hook, den Sie am häufigsten verwenden werden, ist der useState-Hook. Der useState-Hook bietet Ihnen im Wesentlichen einen Setter und einen Getter, um eine einzelne Statuseigenschaft der Komponente zu bearbeiten. Der useState-Hook hat die folgende Signatur:

      Wenn der Hook zum ersten Mal aufgerufen wird, wird das Statuselement mit dem Initialwert initialisiert. Bei nachfolgenden Aufrufen von useState wird der zuvor festgelegte Wert zusammen mit einer Setter-Methode zurückgegeben, mit der ein neuer Wert für diese bestimmte Zustandseigenschaft festgelegt werden kann.

      Lassen Sie uns die TodoInput-Komponente mithilfe des Hooks useState in eine Funktionskomponente konvertieren.

      Wie Sie sehen, verwenden wir useState, um den Textstatus-Eigenschaftswert und den setText-Setter zu erhalten, die von der useState-Methode abgerufen werden. Die Methoden onTextChange und addTodoItem wurden geändert, um die setText-Methode setter anstelle von setState zu verwenden.

      Möglicherweise ist Ihnen aufgefallen, dass der onChange-Ereignishandler für die Eingabe sowie die onClick-Ereignishandler für die Schaltfläche Todo hinzufügen beide anonyme Funktionen sind, die während des Renderns erstellt werden. Wie Sie vielleicht wissen, ist dies nicht wirklich gut für die Leistung, da sich der Verweis auf die Funktionen zwischen den Renderings ändert, sodass React sowohl die Eingabe als auch die Schaltfläche erneut rendern muss.

      Um diese unnötigen Wiedergaben zu vermeiden, müssen wir den Verweis auf diese Funktionen gleich halten. Hier kommt der nächste Haken, den wir useCallback verwenden werden, ins Bild. useCallback hat folgende Signatur:

      woher:

      • inlineFunctionDefinition - ist die Funktion, auf die Sie den Verweis zwischen Rendern beibehalten möchten. Dies kann eine anonyme Inline-Funktion sein oder eine Funktion, die von einem anderen Ort importiert wird. Da wir uns in den meisten Fällen jedoch auf Zustandsvariablen der Komponente beziehen möchten, definieren wir dies als Inline-Funktion, die es uns ermöglicht, auf die Zustandsvariablen über Closures zuzugreifen.
      • memoizationArguments - ist ein Array von Argumenten, auf die die inlineFunctionDefinition-Funktion verweist. Beim ersten Aufruf des useCallback-Hooks werden die memoizationArguments zusammen mit der inlineFunctionDefinition gespeichert. Bei nachfolgenden Aufrufen wird jedes Element im neuen Array memoizationArguments mit dem Wert des Elements am selben Index im zuvor gespeicherten Array memoizationArguments verglichen. Wenn keine Änderung vorliegt, wird die zuvor gespeicherte inlineFunctionDefinition zurückgegeben, wodurch die Referenz erhalten bleibt und ein unnötiges erneutes Rendern verhindert wird. Wenn sich einer der Parameter geändert hat, werden die inlineFunctionDefinition und die neuen memoizationArguments gespeichert und verwendet. Dadurch wird der Verweis auf die Funktion geändert und ein erneutes Rendern sichergestellt.

      So passen Sie TodoInput an, um useCallback zu verwenden:

      Nachdem wir TodoInput in eine Funktionskomponente konvertiert haben, wollen wir dasselbe mit TodoItem tun. Das Umschreiben mit useState und useCallback wird zu TodoItem:

      Wenn Sie dies mit der Klassenkomponentenversion von TodoItem vergleichen, werden Sie feststellen, dass die getDerivedStateFromProps, mit der wir festgestellt haben, ob dieser TodoItem abgeschlossen wurde oder nicht, in der Funktionskomponentenversion fehlt. Mit welchem ​​Haken würden wir das implementieren?

      Nun, es gibt keinen speziellen Haken, um dies zu implementieren. Stattdessen müssen wir dies als Teil der Renderfunktion selbst implementieren. Sobald wir es implementiert haben, sieht TodoItem folgendermaßen aus:

      Möglicherweise stellen Sie fest, dass die setCompleted-Status-Setter-Methode während des Renderns der Komponente aufgerufen wird. Beim Schreiben von Klassenkomponenten rufen wir nie setState in der Render-Methode auf. Warum ist dies in Funktionskomponenten akzeptabel?

      Dies ist in Funktionskomponenten erlaubt, um getDerivedStateFromProps-esque-Aktionen auszuführen. Es ist wichtig zu beachten, dass innerhalb einer Funktionskomponente immer State-Setter-Methoden innerhalb eines Bedingungsblocks aufgerufen werden. Ansonsten enden wir in einer Endlosschleife.

      Bitte beachten Sie, dass ich den isCompleted-Check hier implementiert habe, um das Setzen des Zustands innerhalb der Funktionskomponente zu demonstrieren. Im Idealfall würde der Fertigzustand einfach nicht verwendet, und der berechnete isCompleted-Wert würde verwendet, um den aktivierten Zustand des Kontrollkästchens festzulegen.

      Schließlich haben wir nur die TodoContainer-Komponente für die Konvertierung. Wir müssen state sowie die Lifecycle-Methoden von componentDidMount und componentDidUpdate implementieren.

      Da wir bereits etwas über useState gesehen haben und ich nicht wirklich eine gute Ausrede habe, useReducer zu demonstrieren, werde ich vorgeben, dass der Status der TodoContainer-Komponente zu kompliziert ist, um jede Staatseigenschaft einzeln mit useState zu verwalten, und dass die bessere Option ist useReducer verwenden.

      Die Signatur des useReducer-Hooks sieht folgendermaßen aus:

      woher:

      • reducerFunction - ist die Funktion, die den vorhandenen Status und eine Aktion als Eingabe übernimmt und einen neuen Status als Ausgabe zurückgibt. Dies sollte jedem bekannt sein, der Redux und seine Reduzierer verwendet hat.
      • initialState - Wenn keine stateInitializerFunction bereitgestellt wird, ist dies das Anfangsstatusobjekt für die Komponente. Wenn eine stateInitializerFunction bereitgestellt wird, wird diese als Argument an diese Funktion übergeben.
      • stateInitializerFunction - Eine Funktion, mit der Sie den Zustand der Komponente faul initialisieren können. Der initialState-Parameter wird als Argument an diese Funktion übergeben.

      So konvertieren Sie die TodoContainer-Komponente zur Verwendung von useReducer:

      Als Nächstes müssen Sie den Status der TodoContainer-Komponente in localStorage schreiben, wenn die Komponente aktualisiert wird, ähnlich wie in componentDidUpdate. Dazu verwenden wir den useEffect-Hook. Der useEffect-Hook ermöglicht es uns, eine bestimmte Aktion einzugeben, die nach jedem Rendering der Komponente ausgeführt wird. Ihre Unterschrift lautet:

      Solange Sie sich an die Regeln der Hooks halten, über die wir zuvor gesprochen haben, können Sie einen useEffect-Block an einer beliebigen Stelle in Ihrer Funktionskomponente einfügen. Wenn Sie mehrere useEffect-Blöcke haben, werden diese nacheinander ausgeführt. Die allgemeine Idee von useEffect ist, alle Aktionen auszuführen, die sich nicht direkt auf die Komponente im useEffect-Block auswirken (Beispiel: API-Aufrufe, DOM-Manipulationen usw.).

      Wir können useEffect verwenden, um sicherzustellen, dass nach jedem einzelnen Rendering unseres TodoContainer der Status der Komponente in localStorage geschrieben wird.

      Als Nächstes müssen wir den Status von localStorage wiederherstellen, wenn die Komponente gemountet wird, ähnlich wie bei componentDidMount. Jetzt gibt es keine speziellen Hooks, um dies auszuführen, aber wir können die Lazy-Initialisierungsfunktion useReducer verwenden (die nur beim ersten Rendern aufgerufen wird), um dies zu erreichen.

      Wie Sie sehen, lesen wir den gespeicherten vorherigen Status von localStorage und verwenden diesen, um den Status der TodoContainer-Komponente zu initialisieren, und ahmen so nach, was wir in componentDidMount tun. Und damit haben wir unsere gesamte ToDo-App auf Funktionskomponenten umgestellt.

      Abschließend…

      In diesem Artikel habe ich nur einige der Haken behandelt, die in React standardmäßig verfügbar sind. Abgesehen von diesen "out of the box" -Haken können wir auch eigene benutzerdefinierte Hooks schreiben, um spezifischere Aufgaben auszuführen.

      Bei dieser kleinen Übung bei der Umwandlung von Klassenkomponenten in Funktionskomponenten ist es wichtig, dass zwischen den Lebenszyklusmethoden und den React-Hooks keine Eins-zu-Eins-Zuordnung erfolgt. Während useState und useReducer der Verwendung von setState sehr ähnlich sind, müssen Sie möglicherweise die Art und Weise ändern, in der Sie bestimmte Aufgaben ausführen, damit sie in einer Funktionskomponente arbeiten können (z. B. getDerivedStateFromProps in unserer Todo-App).

      Das Konvertieren einer React-Klassenkomponente in eine Funktionskomponente kann daher trivial sein oder je nach Komponente recht kompliziert sein. Wenn Sie keine neue Komponente schreiben, ist es wirklich nicht schwer, eine Klassenkomponente in eine Funktionskomponente zu konvertieren, zumal React beide Formulare auf absehbare Zeit unterstützen wird.

      Wenn Sie jedoch feststellen müssen, dass Sie eine vorhandene Klassenkomponente in eine Funktionskomponente konvertieren müssen, hoffe ich, dass dieser Artikel einen guten Ausgangspunkt bietet. Ich würde gerne Ihre Meinung zu den Dingen hören, die ich in diesem Artikel geschrieben habe.

      Ursprünglich auf asleepysamurai.com veröffentlicht.

Siehe auch

Mein Leben als Tech-Enthusiast.