v19.2Latest

Synchronisierung mit Effekten

Einige Komponenten müssen sich mit externen Systemen synchronisieren. Beispielsweise möchten Sie möglicherweise eine Nicht-React-Komponente basierend auf dem React-State steuern, eine Serververbindung einrichten oder ein Analyseprotokoll senden, wenn eine Komponente auf dem Bildschirm erscheint.Effekteermöglichen es Ihnen, nach dem Rendern Code auszuführen, sodass Sie Ihre Komponente mit einem System außerhalb von React synchronisieren können.

Sie werden lernen
  • Was Effekte sind
  • Wie sich Effekte von Ereignissen unterscheiden
  • Wie Sie einen Effekt in Ihrer Komponente deklarieren
  • Wie Sie unnötiges erneutes Ausführen eines Effekts vermeiden
  • Warum Effekte in der Entwicklung zweimal laufen und wie Sie dies beheben

Was sind Effekte und wie unterscheiden sie sich von Ereignissen?

Bevor wir zu Effekten kommen, müssen Sie mit zwei Arten von Logik in React-Komponenten vertraut sein:

  • Rendering-Code(eingeführt inBeschreibung der Benutzeroberfläche) befindet sich auf der obersten Ebene Ihrer Komponente. Hier nehmen Sie Props und State, transformieren sie und geben das JSX zurück, das Sie auf dem Bildschirm sehen möchten.Rendering-Code muss rein sein.Wie eine mathematische Formel sollte er nur das Ergebnisberechnen, aber nichts anderes tun.
  • Ereignishandler(eingeführt inHinzufügen von Interaktivität) sind verschachtelte Funktionen innerhalb Ihrer Komponenten, die Dingetun, anstatt sie nur zu berechnen. Ein Ereignishandler könnte ein Eingabefeld aktualisieren, eine HTTP-POST-Anfrage zum Kauf eines Produkts senden oder den Benutzer zu einem anderen Bildschirm navigieren. Ereignishandler enthalten„Nebeneffekte“(sie ändern den Zustand des Programms), die durch eine bestimmte Benutzeraktion verursacht werden (z. B. einen Klick auf eine Schaltfläche oder das Tippen).

Manchmal reicht das nicht aus. Betrachten Sie eineChatRoom-Komponente, die eine Verbindung zum Chat-Server herstellen muss, wann immer sie auf dem Bildschirm sichtbar ist. Das Verbinden mit einem Server ist keine reine Berechnung (es ist ein Nebeneffekt) und kann daher nicht während des Renderings geschehen. Es gibt jedoch kein einzelnes bestimmtes Ereignis wie einen Klick, das dazu führt, dassChatRoomangezeigt wird.

Effekteermöglichen es Ihnen, Nebeneffekte anzugeben, die durch das Rendering selbst verursacht werden, und nicht durch ein bestimmtes Ereignis.Das Senden einer Nachricht im Chat ist einEreignis, da es direkt durch das Klicken des Benutzers auf eine bestimmte Schaltfläche verursacht wird. Das Einrichten einer Serververbindung ist jedoch einEffekt, da es unabhängig davon geschehen sollte, welche Interaktion dazu geführt hat, dass die Komponente erscheint. Effekte werden am Ende einesCommitsausgeführt, nachdem der Bildschirm aktualisiert wurde. Dies ist ein guter Zeitpunkt, um die React-Komponenten mit einem externen System (wie einem Netzwerk oder einer Drittanbieterbibliothek) zu synchronisieren.

Hinweis

Hier und im weiteren Text bezieht sich das großgeschriebene „Effekt“ auf die oben genannte React-spezifische Definition, d. h. einen durch das Rendering verursachten Nebeneffekt. Um auf das allgemeinere Programmierkonzept zu verweisen, sagen wir „Nebeneffekt“.

Sie benötigen möglicherweise keinen Effekt

Beeilen Sie sich nicht, Ihren Komponenten Effekte hinzuzufügen.Denken Sie daran, dass Effekte typischerweise verwendet werden, um aus Ihrem React-Code „auszusteigen“ und sich mit einemexternenSystem zu synchronisieren. Dazu gehören Browser-APIs, Widgets von Drittanbietern, Netzwerke und so weiter. Wenn Ihr Effekt nur einen State basierend auf einem anderen State anpasst,benötigen Sie möglicherweise keinen Effekt.

Wie Sie einen Effekt schreiben

Um einen Effekt zu schreiben, befolgen Sie diese drei Schritte:

  1. Deklarieren Sie einen Effekt.Standardmäßig wird Ihr Effekt nach jedemCommitausgeführt.
  2. Geben Sie die Effekt-Abhängigkeiten an.Die meisten Effekte sollten nur dann erneut ausgeführt werden,wenn es nötig ist, und nicht nach jedem Rendering. Beispielsweise sollte eine Einblendanimation nur ausgelöst werden, wenn eine Komponente erscheint. Das Verbinden und Trennen von einem Chatraum sollte nur geschehen, wenn die Komponente erscheint und verschwindet oder wenn sich der Chatraum ändert. Sie werden lernen, wie Sie dies steuern, indem SieAbhängigkeiten angeben.
  3. Fügen Sie bei Bedarf eine Bereinigung hinzu.Einige Effekte müssen angeben, wie sie das, was sie getan haben, stoppen, rückgängig machen oder bereinigen. Beispielsweise benötigt „Verbinden“ ein „Trennen“, „Abonnieren“ benötigt ein „Kündigen“ und „Abrufen“ benötigt entweder „Abbrechen“ oder „Ignorieren“. Sie werden lernen, wie Sie dies tun, indem Sie eineBereinigungsfunktionzurückgeben.

Schauen wir uns jeden dieser Schritte im Detail an.

Schritt 1: Deklarieren Sie einen Effekt

Um einen Effekt in Ihrer Komponente zu deklarieren, importieren Sie denuseEffect-Hookaus React:

Rufen Sie es dann auf der obersten Ebene Ihrer Komponente auf und fügen Sie etwas Code in Ihren Effekt ein:

Jedes Mal, wenn Ihre Komponente gerendert wird, aktualisiert React den Bildschirmund führt dannden Code innerhalb vonuseEffectaus. Mit anderen Worten:useEffect„verzögert“ die Ausführung eines Codeabschnitts, bis dieses Rendering auf dem Bildschirm sichtbar ist.

Sehen wir uns an, wie Sie einen Effekt verwenden können, um sich mit einem externen System zu synchronisieren. Betrachten Sie eine<VideoPlayer>React-Komponente. Es wäre praktisch, zu steuern, ob sie abgespielt oder pausiert wird, indem man ihr eineisPlaying-Prop übergibt:

Ihre benutzerdefinierteVideoPlayer-Komponente rendert das eingebaute Browser-<video>-Tag:

Allerdings hat das Browser-<video>-Tag keineisPlaying-Prop. Die einzige Möglichkeit, es zu steuern, besteht darin, die Methodenplay()undpause()manuell auf dem DOM-Element aufzurufen.Sie müssen den Wert derisPlaying-Prop, die angibt, ob das Videoaktuellabgespielt werden sollte, mit Aufrufen wieplay()undpause()synchronisieren.

Wir müssen zunächsteine Ref auf den DOM-Knotendes<video>erhalten.

Sie könnten versucht sein,play()oderpause()während des Renderings aufzurufen, aber das ist nicht korrekt:

Der Grund, warum dieser Code nicht korrekt ist, liegt darin, dass er während des Renderns etwas mit dem DOM-Knoten zu tun versucht. In React sollte das Rendern einereine Berechnungvon JSX sein und keine Nebeneffekte wie das Ändern des DOMs enthalten.

Außerdem existiert der DOM noch nicht, wennVideoPlayerzum ersten Mal aufgerufen wird! Es gibt noch keinen DOM-Knoten, auf demplay()oderpause()aufgerufen werden könnte, weil React erst weiß, welchen DOM es erstellen soll, nachdem du das JSX zurückgegeben hast.

Die Lösung hier ist,den Nebeneffekt mituseEffectzu umschließen, um ihn aus der Rendering-Berechnung herauszunehmen:

Indem du die DOM-Aktualisierung in einen Effekt einpackst, lässt du React zuerst den Bildschirm aktualisieren. Dann läuft dein Effekt.

Wenn deineVideoPlayer-Komponente gerendert wird (entweder zum ersten Mal oder bei einem erneuten Rendern), passieren einige Dinge. Zuerst aktualisiert React den Bildschirm und stellt sicher, dass das<video>-Tag mit den richtigen Props im DOM ist. Dann führt React deinen Effekt aus. Schließlich ruft dein Effektplay()oderpause()auf, abhängig vom Wert vonisPlaying.

Drücke mehrmals auf Play/Pause und sieh zu, wie der Videoplayer mit demisPlaying-Wert synchron bleibt:

In diesem Beispiel war das „externe System“, das Sie mit dem React-Zustand synchronisiert haben, die Browser-Medien-API. Sie können einen ähnlichen Ansatz verwenden, um Legacy-Code, der nicht auf React basiert (wie jQuery-Plugins), in deklarative React-Komponenten zu kapseln.

Beachten Sie, dass die Steuerung eines Videoplayers in der Praxis viel komplexer ist. Der Aufruf vonplay()kann fehlschlagen, der Benutzer könnte die integrierten Browser-Steuerelemente zum Abspielen oder Pausieren verwenden und so weiter. Dieses Beispiel ist stark vereinfacht und unvollständig.

Fallstrick

Standardmäßig laufen Effekte nachjedemRendering. Deshalb wird Code wie dieser eineEndlosschleife erzeugen:

Effekte laufen alsFolgeeines Renderings. Das Setzen eines Zustandslöstein Rendering aus. Wenn Sie in einem Effekt sofort den Zustand setzen, ist das, als würden Sie eine Steckdose in sich selbst einstecken. Der Effekt läuft, setzt den Zustand, was ein erneutes Rendering verursacht, wodurch der Effekt wieder läuft, er setzt den Zustand erneut, dies verursacht ein weiteres Rendering und so weiter.

Effekte sollten normalerweise Ihre Komponenten mit einemexternenSystem synchronisieren. Wenn es kein externes System gibt und Sie nur einen Zustand basierend auf einem anderen Zustand anpassen möchten,benötigen Sie möglicherweise keinen Effekt.

Schritt 2: Geben Sie die Effekt-Abhängigkeiten an

Standardmäßig laufen Effekte nachjedemRendering. Oft ist dasnicht das, was Sie wollen:

  • Manchmal ist es langsam. Die Synchronisierung mit einem externen System ist nicht immer sofort möglich, daher möchten Sie sie möglicherweise überspringen, es sei denn, sie ist notwendig. Beispielsweise möchten Sie nicht bei jedem Tastenanschlag eine erneute Verbindung zum Chat-Server herstellen.
  • Manchmal ist es falsch. Beispielsweise möchten Sie nicht bei jedem Tastenanschlag eine Einblend-Animation einer Komponente auslösen. Die Animation sollte nur einmal abgespielt werden, wenn die Komponente zum ersten Mal erscheint.

Um das Problem zu demonstrieren, folgt hier das vorherige Beispiel mit einigenconsole.log-Aufrufen und einer Texteingabe, die den Zustand der übergeordneten Komponente aktualisiert. Beachten Sie, wie das Tippen dazu führt, dass der Effekt erneut ausgeführt wird:

Sie können React anweisen,das unnötige erneute Ausführen des Effekts zu überspringen, indem Sie ein Array vonAbhängigkeitenals zweites Argument für denuseEffect-Aufruf angeben[]hinzuzufügen:

Sie sollten eine Fehlermeldung sehen, die besagt:React Hook useEffect has a missing dependency: 'isPlaying':

Das Problem ist, dass der Code innerhalb Ihres EffektsvonderisPlaying-Prop abhängt, um zu entscheiden, was zu tun ist, aber diese Abhängigkeit wurde nicht explizit deklariert. Um dieses Problem zu beheben, fügen SieisPlayingzum Abhängigkeitsarray hinzu:

Nun sind alle Abhängigkeiten deklariert, daher gibt es keinen Fehler. Die Angabe von[isPlaying]als Abhängigkeitsarray teilt React mit, dass es das erneute Ausführen Ihres Effekts überspringen sollte, wennisPlayingdenselben Wert hat wie während des vorherigen Renders. Mit dieser Änderung führt das Tippen in das Eingabefeld nicht mehr dazu, dass der Effekt erneut ausgeführt wird, aber das Drücken von Play/Pause tut es:

Das Abhängigkeitsarray kann mehrere Abhängigkeiten enthalten. React überspringt die erneute Ausführung des Effekts nur, wennallevon Ihnen angegebenen Abhängigkeiten exakt dieselben Werte haben wie beim vorherigen Rendering. React vergleicht die Abhängigkeitswerte mithilfe desObject.is-Vergleichs. Details finden Sie in deruseEffect-Referenz.

Beachten Sie, dass Sie Ihre Abhängigkeiten nicht „auswählen“ können.Sie erhalten einen Lint-Fehler, wenn die von Ihnen angegebenen Abhängigkeiten nicht mit dem übereinstimmen, was React basierend auf dem Code innerhalb Ihres Effekts erwartet. Dies hilft, viele Fehler in Ihrem Code zu erkennen. Wenn Sie nicht möchten, dass ein Teil des Codes erneut ausgeführt wird,bearbeiten Sie den Effektcode selbst, sodass er diese Abhängigkeit nicht mehr „benötigt“.

Fallstrick

Das Verhalten ohne Abhängigkeitsarray und mit einemleeren[]Abhängigkeitsarray ist unterschiedlich:

Wir werden uns im nächsten Schritt genau ansehen, was „Mount“ bedeutet.

Deep Dive
Warum wurde die Ref aus dem Abhängigkeitsarray weggelassen?

Schritt 3: Bereinigung hinzufügen, falls nötig

Betrachten Sie ein anderes Beispiel. Sie schreiben eineChatRoom-Komponente, die sich beim Erscheinen mit dem Chat-Server verbinden muss. Ihnen wird einecreateConnection()-API gegeben, die ein Objekt mit den Methodenconnect()unddisconnect()zurückgibt. Wie halten Sie die Komponente verbunden, während sie dem Benutzer angezeigt wird?

Beginnen Sie damit, die Effektlogik zu schreiben:

Es wäre langsam, sich nach jedem erneuten Rendern mit dem Chat zu verbinden, daher fügst du das Abhängigkeitsarray hinzu:

Der Code innerhalb des Effekts verwendet keine Props oder State, daher ist dein Abhängigkeitsarray[](leer). Dies teilt React mit, diesen Code nur auszuführen, wenn die Komponente „gemountet“ wird, also zum ersten Mal auf dem Bildschirm erscheint.

Lass uns versuchen, diesen Code auszuführen:

Dieser Effekt läuft nur beim Mounten, daher könnte man erwarten, dass"✅ Connecting..."nur einmal in der Konsole ausgegeben wird.Wenn Sie jedoch die Konsole überprüfen, wird"✅ Connecting..."zweimal ausgegeben. Warum passiert das?

Stellen Sie sich vor, dieChatRoom-Komponente ist Teil einer größeren App mit vielen verschiedenen Bildschirmen. Der Nutzer beginnt seine Reise auf derChatRoom-Seite. Die Komponente wird gemountet und ruftconnection.connect()auf. Dann stellen Sie sich vor, der Nutzer navigiert zu einem anderen Bildschirm – zum Beispiel zur Einstellungsseite. DieChatRoom-Komponente wird ungemountet. Schließlich klickt der Nutzer auf Zurück undChatRoomwird erneut gemountet. Dies würde eine zweite Verbindung aufbauen – aber die erste Verbindung wurde nie zerstört! Wenn der Nutzer durch die App navigiert, würden sich die Verbindungen weiter anhäufen.

Solche Fehler sind ohne umfangreiche manuelle Tests leicht zu übersehen. Um Ihnen zu helfen, sie schnell zu erkennen, remountet React in der Entwicklung jede Komponente einmal unmittelbar nach ihrem initialen Mount.

Wenn Sie die"✅ Connecting..."-Protokollierung zweimal sehen, hilft Ihnen das, das eigentliche Problem zu bemerken: Ihr Code schließt die Verbindung nicht, wenn die Komponente ungemountet wird.

Um das Problem zu beheben, geben Sie eineCleanup-Funktionaus Ihrem Effekt zurück:

React wird Ihre Cleanup-Funktion jedes Mal aufrufen, bevor der Effekt erneut ausgeführt wird, und ein letztes Mal, wenn die Komponente ungemountet wird (entfernt wird). Sehen wir uns an, was passiert, wenn die Cleanup-Funktion implementiert ist:

Jetzt erhalten Sie in der Entwicklung drei Konsolenprotokolle:

  1. "✅ Connecting..."
  2. "❌ Disconnected."
  3. "✅ Connecting..."

Dies ist das korrekte Verhalten in der Entwicklung.Durch das erneute Einhängen (Remounting) deiner Komponente überprüft React, dass das Navigieren weg und zurück deinen Code nicht beschädigen würde. Trennen und dann wieder verbinden ist genau das, was passieren sollte! Wenn du die Cleanup-Funktion gut implementierst, sollte es für den Benutzer keinen sichtbaren Unterschied geben, ob der Effekt einmal läuft oder ob er läuft, bereinigt wird und erneut läuft. Es gibt ein zusätzliches Verbindungs-/Trennungspaar von Aufrufen, weil React in der Entwicklung deinen Code auf Fehler untersucht. Das ist normal – versuche nicht, es zu verhindern!

In der Produktion würdest du nur einmal"✅ Connecting..."ausgegeben sehen.Das erneute Einhängen von Komponenten geschieht nur in der Entwicklung, um dir dabei zu helfen, Effekte zu finden, die eine Cleanup-Funktion benötigen. Du kannst denStrict Modeausschalten, um das Entwicklungsverhalten zu deaktivieren, aber wir empfehlen, ihn eingeschaltet zu lassen. So kannst du viele Fehler wie den oben genannten finden.

Wie geht man damit um, dass der Effekt in der Entwicklung zweimal ausgeführt wird?

React hängt deine Komponenten in der Entwicklung absichtlich erneut ein, um Fehler wie im letzten Beispiel zu finden.Die richtige Frage ist nicht „Wie führe ich einen Effekt einmal aus“, sondern „Wie behebe ich meinen Effekt, damit er nach dem erneuten Einhängen funktioniert“.

Normalerweise lautet die Antwort, die Cleanup-Funktion zu implementieren. Die Cleanup-Funktion sollte das stoppen oder rückgängig machen, was der Effekt getan hat. Die Faustregel ist, dass der Benutzer nicht zwischen einem einmaligen Ausführen des Effekts (wie in der Produktion) und einerSetup → Cleanup → Setup-Sequenz (wie man sie in der Entwicklung sieht) unterscheiden können sollte.

Die meisten Effekte, die du schreibst, werden in eines der folgenden gängigen Muster passen.

Fallstrick

Verwende keine Refs, um zu verhindern, dass Effekte ausgeführt werden

Ein häufiger Fallstrick, um zu verhindern, dass Effekte in der Entwicklung zweimal ausgeführt werden, ist die Verwendung einerref, um zu verhindern, dass der Effekt mehr als einmal läuft. Du könntest beispielsweise den obigen Fehler mit einemuseRef„reparieren“:

Dadurch siehst du in der Entwicklung nur einmal"✅ Connecting...", aber der Fehler wird nicht behoben.

Wenn der Benutzer weg navigiert, wird die Verbindung immer noch nicht geschlossen, und wenn er zurück navigiert, wird eine neue Verbindung erstellt. Wenn der Benutzer durch die App navigiert, würden sich die Verbindungen weiter anhäufen, genau wie vor der „Reparatur“.

Um den Fehler zu beheben, reicht es nicht aus, den Effekt nur einmal ausführen zu lassen. Der Effekt muss nach dem erneuten Einhängen funktionieren, was bedeutet, dass die Verbindung wie in der obigen Lösung bereinigt werden muss.

Siehe die folgenden Beispiele, wie man gängige Muster handhabt.

Steuerung von Nicht-React-Widgets

Manchmal müssen Sie UI-Widgets hinzufügen, die nicht in React geschrieben sind. Angenommen, Sie fügen Ihrer Seite eine Kartenkomponente hinzu. Sie verfügt über eine MethodesetZoomLevel(), und Sie möchten den Zoomlevel mit einer ZustandsvariablezoomLevelin Ihrem React-Code synchron halten. Ihr Effekt würde in etwa so aussehen:

Beachten Sie, dass in diesem Fall keine Bereinigung erforderlich ist. In der Entwicklung wird React den Effekt zweimal aufrufen, aber das ist kein Problem, denn das zweimalige Aufrufen vonsetZoomLevelmit demselben Wert bewirkt nichts. Es könnte etwas langsamer sein, aber das spielt keine Rolle, da es in der Produktion nicht unnötig neu eingehängt wird.

Einige APIs erlauben möglicherweise nicht, sie zweimal hintereinander aufzurufen. Zum Beispiel wirft dieshowModal-Methode des eingebauten<dialog>-Elements einen Fehler, wenn Sie sie zweimal aufrufen. Implementieren Sie die Bereinigungsfunktion und lassen Sie sie das Dialogfeld schließen:

In der Entwicklung wird Ihr EffektshowModal()aufrufen, dann sofortclose()und dann wiedershowModal(). Dies hat das gleiche für den Benutzer sichtbare Verhalten wie das einmalige Aufrufen vonshowModal(), wie Sie es in der Produktion sehen würden.

Abonnieren von Ereignissen

Wenn Ihr Effekt etwas abonniert, sollte die Bereinigungsfunktion das Abonnement kündigen:

In der Entwicklung wird Ihr EffektaddEventListener()aufrufen, dann sofortremoveEventListener()und dann wiederaddEventListener()mit demselben Handler. Es würde also immer nur ein aktives Abonnement gleichzeitig geben. Dies hat das gleiche für den Benutzer sichtbare Verhalten wie das einmalige Aufrufen vonaddEventListener(), wie in der Produktion.

Auslösen von Animationen

Wenn Ihr Effekt etwas animiert einblendet, sollte die Bereinigungsfunktion die Animation auf die Anfangswerte zurücksetzen:

In der Entwicklung wird die Deckkraft auf1gesetzt, dann auf0und dann wieder auf1. Dies sollte das gleiche für den Benutzer sichtbare Verhalten haben wie das direkte Setzen auf1, was in der Produktion geschehen würde. Wenn Sie eine Drittanbieter-Animationbibliothek mit Unterstützung für Tweening verwenden, sollte Ihre Bereinigungsfunktion die Timeline auf ihren Ausgangszustand zurücksetzen.

Abrufen von Daten

Wenn Ihr Effekt etwas abruft, sollte die Bereinigungsfunktion den Abruf entwederabbrechenoder dessen Ergebnis ignorieren:

Sie können eine bereits erfolgte Netzwerkanfrage nicht "rückgängig machen", aber Ihre Bereinigungsfunktion sollte sicherstellen, dass der Abruf, dernicht mehr relevant ist, Ihre Anwendung nicht weiter beeinflusst. Wenn sich dieuserId von 'Alice'zu'Bob'ändert, stellt die Bereinigung sicher, dass die Antwort für'Alice'ignoriert wird, selbst wenn sie nach'Bob'eintrifft.

In der Entwicklung werden Sie zwei Abrufe im Netzwerk-Tab sehen.Daran ist nichts auszusetzen. Mit dem obigen Ansatz wird der erste Effekt sofort bereinigt, sodass seine Kopie derignore-Variable auftruegesetzt wird. Obwohl es also eine zusätzliche Anfrage gibt, wird sie dank derif (!ignore)-Prüfung den Zustand nicht beeinflussen.

In der Produktion gibt es nur eine Anfrage.Wenn Sie die zweite Anfrage in der Entwicklung stört, ist der beste Ansatz, eine Lösung zu verwenden, die Anfragen dedupliziert und ihre Antworten zwischen Komponenten zwischenspeichert:

Dies verbessert nicht nur die Entwicklungserfahrung, sondern lässt Ihre Anwendung auch schneller wirken. Zum Beispiel muss der Benutzer beim Drücken der Zurück-Taste nicht warten, bis einige Daten erneut geladen werden, da sie zwischengespeichert werden. Sie können einen solchen Cache entweder selbst erstellen oder eine der vielen Alternativen zum manuellen Abrufen in Effekten verwenden.

Deep Dive
Welche guten Alternativen gibt es zum Datenabruf in Effects?

Analytics senden

Betrachten Sie diesen Code, der bei einem Seitenbesuch ein Analytics-Ereignis sendet:

In der Entwicklung wirdlogVisitfür jede URL zweimal aufgerufen, daher könnten Sie versucht sein, das zu beheben.Wir empfehlen, diesen Code so zu belassen.Wie in früheren Beispielen gibt es keinenfür den Benutzer sichtbarenVerhaltensunterschied zwischen einmaligem und zweimaligem Ausführen. Aus praktischer Sicht solltelogVisitin der Entwicklung nichts tun, da Sie nicht möchten, dass Logs von Entwicklungsmaschinen die Produktionsmetriken verfälschen. Ihre Komponente wird bei jeder Speicherung ihrer Datei neu eingebunden, sodass in der Entwicklung ohnehin zusätzliche Besuche protokolliert werden.

In der Produktion gibt es keine doppelten Besuchslogs.

Um die gesendeten Analytics-Ereignisse zu debuggen, können Sie Ihre App in einer Staging-Umgebung (die im Produktionsmodus läuft) bereitstellen oder vorübergehend aufStrict Modeund dessen nur in der Entwicklung vorhandene Neubindungsprüfungen verzichten. Sie können Analytics auch von den Routenwechsel-Ereignishandlern anstelle von Effects senden. Für präzisere Analytics könnenIntersection Observerhelfen, zu verfolgen, welche Komponenten sich im Viewport befinden und wie lange sie sichtbar bleiben.

Kein Effect: Initialisierung der Anwendung

Einige Logik sollte nur einmal beim Start der Anwendung ausgeführt werden. Sie können sie außerhalb Ihrer Komponenten platzieren:

Dies stellt sicher, dass solche Logik nur einmal nach dem Laden der Seite im Browser ausgeführt wird.

Kein Effect: Kauf eines Produkts

Manchmal gibt es, selbst wenn Sie eine Cleanup-Funktion schreiben, keine Möglichkeit, die für den Benutzer sichtbaren Konsequenzen einer zweimaligen Ausführung des Effects zu verhindern. Beispielsweise sendet Ihr Effect vielleicht eine POST-Anfrage wie den Kauf eines Produkts:

Sie möchten das Produkt nicht zweimal kaufen. Das ist aber auch der Grund, warum Sie diese Logik nicht in einen Effect packen sollten. Was ist, wenn der Benutzer zu einer anderen Seite geht und dann auf Zurück drückt? Ihr Effect würde erneut ausgeführt werden. Sie möchten das Produkt nicht kaufen, wenn der Benutzer eine Seitebesucht; Sie möchten es kaufen, wenn der Benutzer auf dieKaufen-Schaltfläche klickt.

Der Kauf wird nicht durch Rendering verursacht; er wird durch eine spezifische Interaktion verursacht. Er sollte nur ausgeführt werden, wenn der Benutzer die Schaltfläche drückt.Löschen Sie den Effect und verschieben Sie Ihre/api/buy-Anfrage in den Ereignishandler der Kaufen-Schaltfläche:

Dies zeigt, dass wenn ein erneutes Einhängen die Logik Ihrer Anwendung zerstört, dies normalerweise vorhandene Fehler aufdeckt.Aus Sicht eines Benutzers sollte das Besuchen einer Seite nicht anders sein, als sie zu besuchen, auf einen Link zu klicken und dann Zurück zu drücken, um die Seite erneut anzuzeigen. React überprüft, dass Ihre Komponenten diesem Prinzip folgen, indem es sie in der Entwicklung einmal erneut einhängt.

Alles zusammenfassen

Dieser Spielplatz kann Ihnen helfen, ein „Gefühl“ dafür zu bekommen, wie Effekte in der Praxis funktionieren.

Dieses Beispiel verwendetsetTimeout, um einen Konsolenlog mit dem Eingabetext drei Sekunden nach Ausführung des Effekts zu planen. Die Cleanup-Funktion bricht den ausstehenden Timeout ab. Beginnen Sie mit dem Drücken von „Komponente einhängen“:

Sie werden zunächst drei Logs sehen:Schedule "a" log,Cancel "a" logundSchedule "a" logerneut. Drei Sekunden später wird es auch einen Log geben, derasagt. Wie Sie bereits gelernt haben, kommt das zusätzliche Planen/Abbruch-Paar daher, dass React die Komponente in der Entwicklung einmal erneut einhängt, um zu überprüfen, ob Sie das Cleanup gut implementiert haben.

Bearbeiten Sie nun die Eingabe, umabceinzugeben. Wenn Sie schnell genug sind, werden SieSchedule "ab" logunmittelbar gefolgt vonCancel "ab" logundSchedule "abc" logsehen.React räumt immer den Effekt des vorherigen Renderings auf, bevor der Effekt des nächsten Renderings ausgeführt wird.Deshalb gibt es selbst bei schneller Eingabe höchstens einen gleichzeitig geplanten Timeout. Bearbeiten Sie die Eingabe einige Male und beobachten Sie die Konsole, um ein Gefühl dafür zu bekommen, wie Effekte aufgeräumt werden.

Geben Sie etwas in das Eingabefeld ein und drücken Sie dann sofort „Komponente aushängen“. Beachten Sie, wie das Aushängen den Effekt des letzten Renderings aufräumt. Hier wird der letzte Timeout gelöscht, bevor er ausgelöst werden kann.

Kommentieren Sie schließlich die Cleanup-Funktion in der obigen Komponente aus, damit die Timeouts nicht abgebrochen werden. Versuchen Sie, schnellabcdeeinzugeben. Was erwarten Sie in drei Sekunden zu passieren? Wirdconsole.log(text)innerhalb des Timeouts denneuestentextausgeben und fünfabcdeLogs erzeugen? Probieren Sie es aus, um Ihre Intuition zu überprüfen!

Drei Sekunden später sollten Sie eine Sequenz von Logs sehen (a,ab,abc,abcdundabcde) und nicht fünfabcdeLogs.Jeder Effekt „erfasst“ dentext-Wert aus seinem entsprechenden Rendering.Es spielt keine Rolle, dass sich dertext-Status geändert hat: Ein Effekt aus dem Rendering mittext = 'ab'

Deep Dive
Jeder Render hat seine eigenen Effekte

Zusammenfassung

  • Im Gegensatz zu Ereignissen werden Effekte durch das Rendering selbst verursacht, nicht durch eine bestimmte Interaktion.
  • Effekte ermöglichen es Ihnen, eine Komponente mit einem externen System (Drittanbieter-API, Netzwerk usw.) zu synchronisieren.
  • Standardmäßig laufen Effekte nach jedem Rendering (einschließlich des ersten).
  • React überspringt den Effekt, wenn alle seine Abhängigkeiten die gleichen Werte wie beim letzten Rendering haben.
  • Sie können Ihre Abhängigkeiten nicht „auswählen“. Sie werden durch den Code innerhalb des Effekts bestimmt.
  • Ein leeres Abhängigkeitsarray ([]) entspricht dem „Mounten“ der Komponente, d.h. dem Hinzufügen zum Bildschirm.
  • Im Strict Mode mountet React Komponenten zweimal (nur in der Entwicklung!), um Ihre Effekte zu belastungstesten.
  • Wenn Ihr Effekt aufgrund des erneuten Mountens fehlschlägt, müssen Sie eine Cleanup-Funktion implementieren.
  • React ruft Ihre Cleanup-Funktion auf, bevor der Effekt das nächste Mal läuft, und während des Unmounts.

Try out some challenges

Challenge 1 of 4:Focus a field on mount #

In this example, the form renders a <MyInput /> component.

Use the input’s focus() method to make MyInput automatically focus when it appears on the screen. There is already a commented out implementation, but it doesn’t quite work. Figure out why it doesn’t work, and fix it. (If you’re familiar with the autoFocus attribute, pretend that it does not exist: we are reimplementing the same functionality from scratch.)

To verify that your solution works, press “Show form” and verify that the input receives focus (becomes highlighted and the cursor is placed inside). Press “Hide form” and “Show form” again. Verify the input is highlighted again.

MyInput should only focus on mount rather than after every render. To verify that the behavior is right, press “Show form” and then repeatedly press the “Make it uppercase” checkbox. Clicking the checkbox should not focus the input above it.