Ein tieferer Blick auf die beliebtesten React-Haken
Pawel Rybczynski
Software Engineer
In vielen Gesprächen habe ich festgestellt, dass selbst erfahrene Programmierer ein Problem damit haben, Hooks zu erkennen, ganz zu schweigen von ihren fortgeschrittenen Fähigkeiten. Daher werde ich versuchen, in diesem Artikel zu erklären, wie Hooks verwendet werden sollten.
Das Wichtigste, was Sie über Hooks wissen müssen:
sie können nur in Funktionskomponenten verwendet werden - Klassenkomponenten haben eine eigene Lebenszyklus-Implementierung;
sie beginnen immer mit verwenden.;
Sie können so viele Hooks verwenden, wie Sie wollen, aber Sie müssen bedenken, dass deren Verwendung Auswirkungen auf die Gesamtleistung hat;
sie müssen bei jedem Rendering in der gleichen Reihenfolge ausgeführt werden...aber warum? Schauen wir uns ein Beispiel an:
Zunächst werden Sie eine Warnung von eslint erhalten:
<code>srcFunctionComponent.js
Zeile 11:5: React Hook "useEffect" wird nur bedingt aufgerufen. <strong>React-Haken</strong> müssen in jeder Komponente in der gleichen Reihenfolge aufgerufen werden .eslintreact-hooks/rules-of-hooks
Wie Sie sehen können, handelt es sich nur um eine eslint-Warnung, die Sie deaktivieren können, indem Sie einen Befehl von unten am Anfang der Datei FunktionsKomponente
/* eslint-disable react-hooks/rules-of-hooks */
und es wird funktionieren, aber nur bis wir die Bedingung erfüllen, die unseren Hook ausführt. Das nächste, was wir sehen werden, ist dieser Fehler.
Unerwarteter Fehler: Es wurden mehr Haken gerendert als beim vorherigen Rendering.
React 5
FunktionsKomponente FunktionsKomponente.js:11
React 12
unstable_runWithPriority scheduler.development.js:468
React 17
js index.js:7
js main.chunk.js:905
Webpack 7
react-dom.entwicklung.js:15162
Warum passiert das? React verlässt sich auf die Reihenfolge, in der die Hooks aufgerufen werden, da React nicht weiß, was es für den useEffect zurückgeben soll, weil es keinen solchen Hook in der zu prüfenden Zeile gibt.
Denken Sie daran, dass eslint ein mächtiges Werkzeug ist, das uns hilft, viele potenzielle Bugs und Fehler zu finden. Die Deaktivierung seiner Warnungen ist eine gefährliche Sache. Prüfen Sie immer, ob das Ignorieren der Warnung einen Absturz der Anwendung verursachen kann.
useState
Sie wissen wahrscheinlich, wie es aussieht 😉
const [value, setValue] = useState(0);
Sie haben also 4 Elemente: Zustand (reaktiver Wert), Aktualisierungsfunktion (Setter), tatsächlicher Haken (Funktion) und optionaler Anfangswert. Warum wird ein Array zurückgegeben? Weil wir es nach Belieben umstrukturieren können.
Jetzt möchte ich mich auf das letzte Element konzentrieren - den Anfangswert. Es gibt zwei Möglichkeiten, den Ausgangszustand zu übergeben:
Durch einen fest kodierten Wert oder etwas anderes - der bei jedem Rendering aufgerufen wird
const [value, setValue] = useState(0);
Durch eine Funktionsversion. Sie ist sehr hilfreich, wenn wir den Ausgangszustand nur einmal, beim allerersten Rendern, ausführen wollen. Vielleicht müssen Sie eine Menge komplexer Berechnungen durchführen, um den Anfangszustand zu erhalten? Das wird die Ressourcenkosten deutlich senken, juhu!
Wie kann man überprüfen, ob der erste Weg wirklich bei jedem Rendering aufgerufen wird? Erstellen Sie eine Funktion und übergeben Sie sie als Ausgangszustand:
Eine weitere Sache, die zu Fehlern im App-Flow führen kann: Sie wissen wahrscheinlich, wie man einen Status aktualisiert, oder?
setValue(1);
Richtig... aber was ist, wenn ich den Status auf der Grundlage eines früheren Status aktualisieren möchte?
setValue(value + 1);
Ja... Aber nein... Was ist, wenn Sie versuchen, die Setter-Funktion zweimal nacheinander aufzurufen? Der empfohlene Weg, einen Zustand auf der Grundlage des vorherigen Zustands zu aktualisieren, ist die Verwendung einer Funktion. Sie garantiert, dass Sie sich auf den vorherigen Zustand beziehen.
Dieser Hook nimmt 2 Argumente entgegen (das zweite ist optional) und wir verwenden ihn, um Seiteneffekte zu behandeln. Und je nachdem, was wir als zweites Argument übergeben, wird der Hook anders aufgerufen:
ohne zweites Argument - jede Wiedergabe
useEffect(() => {
doSomething();
});
leeres Feld - nur beim ersten Rendering
useEffect(() => {
doSomething();
}, []);
Array mit Abhängigkeiten - jedes Mal, wenn sich der Wert im Abhängigkeits-Array ändert
useEffect(() => {
doSomething(value);
}, [Wert]);
Aufräumen
Mit useEffect können wir etwas verwenden, das als cleanup bezeichnet wird. Wofür ist das gut? Es ist sehr nützlich, aber ich denke, es ist am besten für die Bereinigung von Ereignis-Listenern geeignet. Nehmen wir an, Sie wollen einen Ereignis-Listener erstellen, der von einem bestimmten Zustand abhängt. Man möchte nicht bei jeder Zustandsänderung einen neuen Event-Listener hinzufügen, denn nach ein paar Rendervorgängen wird es so viele Listener geben, dass es die Leistung der App beeinträchtigt. Eine gute Möglichkeit, solche Dinge zu vermeiden, ist die Verwendung der Aufräumfunktion. Wie man das macht? Fügen Sie einfach eine Rückgabefunktion zu useEffect.
Da sie sich innerhalb des useEffect-Hooks befindet, wird die Rückgabe abhängig vom Abhängigkeits-Array aufgerufen - bei jedem Rendering, nur beim ersten Rendering oder wenn sich der Wert im Abhängigkeits-Array ändert. Aber wenn die Komponente abgehängt wird, wird cleaning auf dem zweiten Argument aufgerufen, egal was passiert. Die Rückgabe Code wird vor dem eigentlichen Code von Hook aufgerufen. Das ist sehr logisch - erst den alten löschen, dann einen neuen erstellen. Richtig?
Zunächst erhalten Sie also eine entfernen Nachricht, dann hinzufügen.
Es gibt eine Sache, auf die Sie achten sollten, wenn Sie useEffect und asynchronen Code darin verwenden. Werfen Sie einen Blick auf den folgenden Code:
Auf den ersten Blick sieht es gut aus. Sie holen einige Daten ab, und wenn die Daten kommen, aktualisieren Sie den Status. Und hier ist die Falle:
Manchmal werden Sie eine solche Warnung erhalten: Eine React-Zustandsaktualisierung kann bei einer nicht eingehängten Komponente nicht durchgeführt werden. Dies ist ein No-op, aber es deutet auf ein Speicherleck in Ihrer Anwendung hin. Um dies zu beheben, brechen Sie alle Abonnements und asynchronen Aufgaben in einer useEffect-Bereinigungsfunktion ab.
Der Grund dafür ist, dass die Komponente in der Zwischenzeit ausgehängt werden kann, die Anwendung aber immer noch versucht, den Status dieser Komponente zu aktualisieren, nachdem das Versprechen erfüllt wurde. Wie geht man damit um? Sie müssen prüfen, ob die Komponente existiert.
Hinweis: Es gibt einen sehr ähnlichen Hook => useLayoutEffect() - der Callback läuft nach dem Rendern der Komponente, aber bevor das Dom visuell aktualisiert wird. Es ist nützlich, wenn Sie mit getBoundingClientRect() arbeiten, aber Sie sollten standardmäßig useEffect verwenden. Warum? Weil es visuelle Aktualisierungen blockieren kann - wenn Sie einen komplexen Code in Ihrem Effekt Hook haben.
useContext
Wozu ist das gut? Gemeinsame Nutzung von Daten ohne Übergabe von Requisiten. Besteht aus den folgenden Elementen:
Geschaffener Kontext - Daten
Context Provider - bietet allen Kindern Kontext
Übergebener Wert - Daten, die Sie weitergeben möchten
In Child müssen Sie den Kontext importieren, den useContext-Hook aufrufen und den Kontext als Argument übergeben.
importiere { UserContext } von "./App";
const { name } = useContext(UserContext);
return <h1>Hallo {Name}<>
```
Voilà. Sieht cool aus. Hauptsächlich zur Übergabe globaler Daten wie Themen usw. Nicht empfohlen für die Verwendung in Aufgaben mit sehr dynamischen Änderungen.
Natürlich können wir stattdessen einen benutzerdefinierten Kontextanbieter und einen benutzerdefinierten Hook erstellen, um die Boilerplate zu reduzieren... aber mit benutzerdefinierten Hooks werde ich mich im nächsten Artikel beschäftigen.
useReducer
Es erlaubt uns, den Zustand zu verwalten und neu zu rendern, wenn sich der Zustand ändert - wie useState. Es ist ähnlich wie der redux reducer. Dieser ist besser als useState, wenn die Zustandslogik komplizierter ist.
Wozu dient sie? Sie kann verwendet werden, wenn wir den Zustand auf seinen Anfangswert zurücksetzen wollen. Nachstehend finden Sie ein schönes Beispiel:
Wann ist sie zu verwenden? Wenn wir eine referenzielle Gleichheit erreichen wollen (wodurch die Anzahl der erstellten Funktionen reduziert wird). Dieser Hook gibt die Funktion zurück, im Gegensatz zu useMemo, das den Wert zurückgibt.
Beispiel: Erstellen Sie eine Funktion in der übergeordneten Komponente und übergeben Sie sie dann über props
Prüfen Sie dann in der untergeordneten Komponente, wie oft der Effekt Hook nach dem Hinzufügen dieser Funktion zum Abhängigkeits-Array aufgerufen wird:
Es wird bei jedem Rendering in die Konsole protokolliert! Selbst wenn die Werte innerhalb der getSquaredValue() Funktion hat sich nicht geändert. Wir können dies jedoch vermeiden, indem wir die Funktion in useCallback einschließen
Bei der Betrachtung der Ressourcenkosten ist es nicht neutral - useMemo muss bei jedem Rendering aufgerufen werden, speichert den Wert im Speicher und vergleicht ihn (Speicher-Overhead),
verwendet Memoization - die Optimierungstechnik, eine spezielle Form des Caching.
Sie sollten es nur in 2 Szenarien verwenden:
Wenn Sie verhindern wollen, dass bei jedem Rendering ein komplexer Code aufgerufen wird;
Wenn Sie referenzielle Gleichheit erreichen wollen.
Schauen wir uns den zweiten Fall etwas genauer an. Wir wollen useEffect mit einem Objekt als Abhängigkeit verwenden. Da Objekte anhand ihrer Referenz verglichen werden, wird useEffect bei jedem Rendering aufgerufen. Um so etwas zu vermeiden, können wir useEffect mit useMemo kombinieren, um solche Objekte zu memoisieren und dann die memoisierten Objekte an das Abhängigkeits-Array zu übergeben. Kurzes Beispiel:
Versuchen Sie zunächst, es ohne useMemo zu machen:
Das 'hobbit'-Objekt sorgt dafür, dass sich die Abhängigkeiten des useEffect-Hakens (Zeile 49) bei jedem Rendering ändern. Verschieben Sie es innerhalb des useEffect Callbacks. Alternativ, verpacken Sie die Initialisierung von 'hobbit' in seinen eigenen useMemo () Hook.eslintreact-hooks/exhaustive-deps
Das Wichtigste: useRef löst kein Re-Rendering aus (wie useState), weil es nicht mit dem Rendering-Zyklus verbunden ist - es behält die gleiche Referenz zwischen den Renderings.
const ref = useRef(0);
Um den gespeicherten Wert aufzurufen, müssen Sie eine aktuelle Eigenschaft verwenden (ref ist ein Objekt) - ref.aktuell
Der zweite Fall, für den wir diesen Hook verwenden können, ist die Referenzierung von Elementen innerhalb von HTML. Jedes Element hat ein ref-Attribut. So können wir Fokus, Ereignisse usw. behandeln.
Der dritte Fall ist, dass wir refs verwenden können, um unkontrollierte Komponenten zu behandeln. Sie können mehr darüber lesen in Reaktordokumente, aber kurz gesagt, sieht es so aus:
Wie Sie sehen können, gibt es keinen Event-Handler, sondern es wird nur der eingegebene Wert gespeichert. Es ist ideal für die Handhabung von einfachen Formularen, wenn Sie nur gespeicherte Werte lesen wollen, wenn Sie sie brauchen (wie beim Absenden).
Bonus: Es ist großartig, wenn Sie sich an frühere Statuswerte erinnern müssen. Sie können dafür den useEffect Hook verwenden, übergeben Sie einfach den Zustand an den ref.
Wie Sie sehen können, sind Hooks nicht so offensichtlich. Wir können sie kombinieren, um viele Probleme zu lösen. Die Beschäftigung mit diesem Thema wird Ihnen sicher viel bringen.
Und es gibt auch benutzerdefinierte Häkchen...
Zusammengefasst, React-Haken haben die Art und Weise revolutioniert React Entwickler Ansatzgebäude Webanwendungen . Durch die Bereitstellung einer intuitiveren und effizienteren Methode zur Verwaltung von Status und Lebenszyklus in funktionalen Komponenten sind Hooks zu einem integralen Bestandteil der React Entwicklung .
Unabhängig davon, ob Sie ein erfahrener Entwickler sind oder gerade erst mit React anfangen, ist es wichtig, die beliebtesten Hooks und ihre Anwendungsfälle zu verstehen. Mit Hooks wie useState, useEffect, useContext und mehr, React-Bestandteile kann mit saubererem und wiederverwendbarem Code erstellt werden. Darüber hinaus kann die Fähigkeit zur Erstellung von Spezialhaken ermöglicht es Entwicklern, Logik zu kapseln und über mehrere Komponenten hinweg gemeinsam zu nutzen, was die Wiederverwendbarkeit und Modularität des Codes fördert. Mit der Weiterentwicklung von React und der Einführung neuer Funktionen werden Hooks zweifellos eine zentrale Rolle bei der Nutzung des vollen Potenzials des Frameworks spielen.
Ganz gleich, ob Sie an einer kleinen Funktionsanwendung oder einer groß angelegten Webanwendung arbeiten, die Einbeziehung von React-Haken wird Ihren Entwicklungs-Workflow verbessern und eine Fülle von Möglichkeiten zur Erstellung robuster und funktionsreicher React-Anwendungen .