v19.2Latest

Ereignisse von Effekten trennen

Ereignishandler werden nur erneut ausgeführt, wenn Sie dieselbe Interaktion wiederholen. Im Gegensatz zu Ereignishandlern synchronisieren sich Effekte erneut, wenn ein von ihnen gelesener Wert, wie eine Prop oder eine Zustandsvariable, sich vom Wert beim letzten Rendering unterscheidet. Manchmal möchten Sie auch eine Mischung aus beiden Verhaltensweisen: einen Effekt, der als Reaktion auf einige Werte erneut läuft, aber nicht auf andere. Diese Seite zeigt Ihnen, wie das geht.

Sie werden lernen
  • Wie Sie zwischen einem Ereignishandler und einem Effekt wählen
  • Warum Effekte reaktiv sind und Ereignishandler nicht
  • Was zu tun ist, wenn ein Teil Ihres Effektcodes nicht reaktiv sein soll
  • Was Effekt-Ereignisse sind und wie Sie sie aus Ihren Effekten extrahieren
  • Wie Sie die neuesten Props und den Zustand aus Effekten mithilfe von Effekt-Ereignissen lesen

Wahl zwischen Ereignishandlern und Effekten

Zunächst fassen wir den Unterschied zwischen Ereignishandlern und Effekten zusammen.

Stellen Sie sich vor, Sie implementieren eine Chatraum-Komponente. Ihre Anforderungen sehen so aus:

  1. Ihre Komponente sollte sich automatisch mit dem ausgewählten Chatraum verbinden.
  2. Wenn Sie auf die Schaltfläche „Senden“ klicken, sollte eine Nachricht an den Chat gesendet werden.

Nehmen wir an, Sie haben den Code dafür bereits implementiert, sind sich aber nicht sicher, wo Sie ihn platzieren sollen. Sollten Sie Ereignishandler oder Effekte verwenden? Jedes Mal, wenn Sie diese Frage beantworten müssen, überlegen Siewarum der Code ausgeführt werden muss.

Ereignishandler laufen als Reaktion auf spezifische Interaktionen

Aus Sicht des Benutzers sollte das Senden einer Nachrichtweildie spezifische „Senden“-Schaltfläche geklickt wurde, erfolgen. Der Benutzer wäre ziemlich verärgert, wenn Sie seine Nachricht zu einem anderen Zeitpunkt oder aus einem anderen Grund senden würden. Deshalb sollte das Senden einer Nachricht ein Ereignishandler sein. Ereignishandler ermöglichen es Ihnen, spezifische Interaktionen zu behandeln:

Mit einem Ereignishandler können Sie sicher sein, dasssendMessage(message) nurausgeführt wird, wenn der Benutzer die Schaltfläche drückt.

Effekte laufen, wann immer Synchronisierung benötigt wird

Denken Sie daran, dass Sie die Komponente auch mit dem Chatraum verbunden halten müssen. Wo kommt dieser Code hin?

DerGrund, diesen Code auszuführen, ist keine bestimmte Interaktion. Es spielt keine Rolle, warum oder wie der Nutzer zum Chatraum-Bildschirm navigiert ist. Jetzt, da er ihn betrachtet und mit ihm interagieren könnte, muss die Komponente mit dem ausgewählten Chat-Server verbunden bleiben. Selbst wenn die Chatraum-Komponente der Startbildschirm Ihrer App wäre und der Nutzer überhaupt keine Interaktionen ausgeführt hätte, müssten Sietrotzdemeine Verbindung herstellen. Deshalb handelt es sich um einen Effekt:

Mit diesem Code können Sie sicher sein, dass immer eine aktive Verbindung zum aktuell ausgewählten Chat-Server besteht,unabhängigvon den spezifischen Interaktionen, die der Nutzer ausführt. Ob der Nutzer Ihre App nur geöffnet hat, einen anderen Raum ausgewählt hat oder zu einem anderen Bildschirm und zurück navigiert ist – Ihr Effekt stellt sicher, dass die Komponentesynchronisiert bleibtmit dem aktuell ausgewählten Raum undbei Bedarf erneut eine Verbindung herstellt.

Reaktive Werte und reaktive Logik

Intuitiv könnte man sagen, dass Event-Handler immer „manuell“ ausgelöst werden, zum Beispiel durch einen Klick auf einen Button. Effekte hingegen sind „automatisch“: Sie werden so oft ausgeführt und erneut ausgeführt, wie nötig ist, um synchronisiert zu bleiben.

Es gibt eine präzisere Art, darüber nachzudenken.

Props, State und Variablen, die im Hauptteil Ihrer Komponente deklariert sind, werdenreaktive Wertegenannt. In diesem Beispiel istserverUrlkein reaktiver Wert, aberroomIdundmessagesind es. Sie nehmen am Rendering-Datenfluss teil:

Reaktive Werte wie diese können sich durch ein erneutes Rendern ändern. Zum Beispiel könnte der Benutzer diemessagebearbeiten oder eine andereroomIdin einem Dropdown-Menü auswählen. Event-Handler und Effekte reagieren unterschiedlich auf Änderungen:

  • Logik innerhalb von Event-Handlern istnicht reaktiv.Sie wird nicht erneut ausgeführt, es sei denn, der Benutzer führt dieselbe Interaktion (z. B. einen Klick) erneut aus. Event-Handler können reaktive Werte lesen, ohne auf deren Änderungen zu „reagieren“.
  • Logik innerhalb von Effekten istreaktiv.Wenn dein Effekt einen reaktiven Wert liest,musst du ihn als Abhängigkeit angeben.Wenn dann ein erneutes Rendern dazu führt, dass sich dieser Wert ändert, führt React die Logik deines Effekts mit dem neuen Wert erneut aus.

Lass uns das vorherige Beispiel noch einmal aufgreifen, um diesen Unterschied zu veranschaulichen.

Logik innerhalb von Event-Handlern ist nicht reaktiv

Sieh dir diese Codezeile an. Sollte diese Logik reaktiv sein oder nicht?

Aus Sicht des Benutzers bedeuteteine Änderung dermessage nicht, dass er eine Nachricht senden möchte.Es bedeutet nur, dass der Benutzer tippt. Mit anderen Worten, die Logik, die eine Nachricht sendet, sollte nicht reaktiv sein. Sie sollte nicht erneut ausgeführt werden, nur weil sich derreaktive Wertgeändert hat. Deshalb gehört sie in den Event-Handler:

Event-Handler sind nicht reaktiv, daher wirdsendMessage(message)nur ausgeführt, wenn der Benutzer auf die Senden-Schaltfläche klickt.

Logik innerhalb von Effekten ist reaktiv

Kehren wir nun zu diesen Zeilen zurück:

Aus Sicht des Benutzers bedeuteteine Änderung derroomIdtatsächlich, dass er sich mit einem anderen Raum verbinden möchte.Mit anderen Worten, die Logik zum Verbinden mit dem Raum sollte reaktiv sein. Dumöchtest, dass diese Codezeilen mit demreaktiven Wert„Schritt halten“ und erneut ausgeführt werden, wenn dieser Wert anders ist. Deshalb gehört sie in einen Effekt:

Effekte sind reaktiv, daher werdencreateConnection(serverUrl, roomId)undconnection.connect()für jeden unterschiedlichen Wert vonroomIdausgeführt. Dein Effekt hält die Chat-Verbindung mit dem aktuell ausgewählten Raum synchronisiert.

Nicht-reaktive Logik aus Effekten extrahieren

Es wird kniffliger, wenn du reaktive Logik mit nicht-reaktiver Logik vermischen möchtest.

Stell dir zum Beispiel vor, du möchtest eine Benachrichtigung anzeigen, wenn der Benutzer sich mit dem Chat verbindet. Du liest das aktuelle Theme (dunkel oder hell) aus den Props, um die Benachrichtigung in der richtigen Farbe anzuzeigen:

Allerdings istthemeein reaktiver Wert (er kann sich durch erneutes Rendern ändern), undjeder von einem Effekt gelesene reaktive Wert muss als seine Abhängigkeit deklariert werden.Jetzt musst duthemeals Abhängigkeit deines Effekts angeben:

Probiere dieses Beispiel aus und sieh, ob du das Problem mit dieser Benutzererfahrung erkennen kannst:

Wenn sich dieroomIdändert, verbindet sich der Chat wie erwartet neu. Da aber auchthemeeine Abhängigkeit ist, verbindet sich der Chatauchjedes Mal neu, wenn du zwischen dem dunklen und dem hellen Thema wechselst. Das ist nicht ideal!

Mit anderen Worten, dumöchtest nicht, dass diese Zeile reaktiv ist, obwohl sie sich innerhalb eines Effects befindet (der reaktiv ist):

Du brauchst eine Möglichkeit, diese nicht-reaktive Logik von dem reaktiven Effect, der sie umgibt, zu trennen.

Deklarieren eines Effect Events

Verwende einen speziellen Hook namensuseEffectEvent, um diese nicht-reaktive Logik aus deinem Effect zu extrahieren:

Hier wirdonConnected als Effect Eventbezeichnet. Es ist Teil Ihrer Effect-Logik, verhält sich aber viel mehr wie ein Event-Handler. Die darin enthaltene Logik ist nicht reaktiv und "sieht" immer die neuesten Werte Ihrer Props und Ihres States.

Jetzt können Sie dasonConnectedEffect Event innerhalb Ihres Effects aufrufen:

Dies löst das Problem. Beachten Sie, dass Sieentfernenthemeaus der Liste der Abhängigkeiten Ihres Effects mussten, da es im Effect nicht mehr verwendet wird. Sie müssen auchnichtonConnectedhinzufügen, weilEffect Events nicht reaktiv sind und von den Abhängigkeiten ausgeschlossen werden müssen.

Überprüfen Sie, ob das neue Verhalten wie erwartet funktioniert:

Sie können sich Effect Events als sehr ähnlich zu Event-Handlern vorstellen. Der Hauptunterschied besteht darin, dass Event-Handler als Reaktion auf Benutzerinteraktionen ausgeführt werden, während Effect Events von Ihnen aus Effects heraus ausgelöst werden. Effect Events ermöglichen es Ihnen, die "Kette" zwischen der Reaktivität von Effects und Code, der nicht reaktiv sein sollte, zu unterbrechen.

Lesen der neuesten Props und des States mit Effect Events

Effect Events ermöglichen es Ihnen, viele Muster zu beheben, bei denen Sie versucht sein könnten, den Dependency-Linter zu unterdrücken.

Angenommen, Sie haben einen Effect, der Seitenbesuche protokolliert:

Später fügst du mehrere Routen zu deiner Website hinzu. Jetzt erhält deinePage-Komponente eineurl-Prop mit dem aktuellen Pfad. Du möchtest dieurlals Teil deineslogVisit-Aufrufs übergeben, aber der Dependency-Linter beschwert sich:

Überlege, was der Code tun soll. Dumöchtestseparate Besuche für verschiedene URLs protokollieren, da jede URL eine andere Seite repräsentiert. Mit anderen Worten, dieserlogVisit-Aufrufsolltein Bezug auf dieurlreaktiv sein. Deshalb ist es in diesem Fall sinnvoll, dem Dependency-Linter zu folgen undurlals Abhängigkeit hinzuzufügen:

Nehmen wir nun an, du möchtest die Anzahl der Artikel im Warenkorb zusammen mit jedem Seitenbesuch erfassen:

Du hastnumberOfItemsinnerhalb des Effects verwendet, daher fordert der Linter dich auf, es als Abhängigkeit hinzuzufügen. Allerdingsmöchtest du nicht, dass derlogVisit-Aufruf in Bezug aufnumberOfItemsreaktiv ist. Wenn der Benutzer etwas in den Warenkorb legt und sichnumberOfItemsändert,bedeutet das nicht, dass der Benutzer die Seite erneut besucht hat. Mit anderen Worten istdas Besuchen der Seitein gewissem Sinne ein „Ereignis“. Es geschieht zu einem bestimmten Zeitpunkt.

Teile den Code in zwei Teile auf:

Hier istonVisitein Effect Event. Der Code darin ist nicht reaktiv. Deshalb kannst dunumberOfItems(oder jeden anderen reaktiven Wert!) verwenden, ohne befürchten zu müssen, dass dies dazu führt, dass der umgebende Code bei Änderungen erneut ausgeführt wird.

Andererseits bleibt der Effect selbst reaktiv. Der Code innerhalb des Effects verwendet dieurl-Prop, daher wird der Effect nach jedem Re-Render mit einer anderenurlerneut ausgeführt. Dies wiederum ruft dasonVisitEffect Event auf.

Dadurch wirst dulogVisitfür jede Änderung derurlaufrufen und dabei immer den neuestennumberOfItemslesen. Wenn sich jedochnumberOfItemsselbständig ändert, führt dies nicht zu einer erneuten Ausführung des Codes.

Hinweis

Du fragst dich vielleicht, ob duonVisit()ohne Argumente aufrufen und dieurldarin lesen könntest:

Das würde funktionieren, aber es ist besser, dieseurlexplizit an das Effect Event zu übergeben.

Einschränkungen von Effect Events

Effect Events sind in ihrer Verwendung sehr eingeschränkt:

  • Rufe sie nur innerhalb von Effects auf.
  • Übergib sie niemals an andere Komponenten oder Hooks.

Deklariere und übergib ein Effect Event zum Beispiel nicht so:

Deklariere Effect Events stattdessen immer direkt neben den Effects, die sie verwenden:

Effect Events sind nicht-reaktive "Teile" deines Effect-Codes. Sie sollten sich neben dem Effect befinden, der sie verwendet.

Zusammenfassung

  • Event-Handler werden als Reaktion auf bestimmte Interaktionen ausgeführt.
  • Effects werden ausgeführt, wann immer Synchronisierung erforderlich ist.
  • Logik innerhalb von Event-Handlern ist nicht reaktiv.
  • Logik innerhalb von Effects ist reaktiv.
  • Du kannst nicht-reaktive Logik aus Effects in Effect Events verschieben.
  • Rufe Effect Events nur innerhalb von Effects auf.
  • Übergib Effect Events nicht an andere Komponenten oder Hooks.

Try out some challenges

Challenge 1 of 4:Fix a variable that doesn’t update #

This Timer component keeps a count state variable which increases every second. The value by which it’s increasing is stored in the increment state variable. You can control the increment variable with the plus and minus buttons.

However, no matter how many times you click the plus button, the counter is still incremented by one every second. What’s wrong with this code? Why is increment always equal to 1 inside the Effect’s code? Find the mistake and fix it.