v19.2Latest

Sie benötigen möglicherweise keinen Effect

Effects sind eine Notlösung, um aus dem React-Paradigma auszubrechen. Sie ermöglichen es Ihnen, "aus React herauszutreten" und Ihre Komponenten mit einem externen System wie einem Nicht-React-Widget, einem Netzwerk oder dem Browser-DOM zu synchronisieren. Wenn kein externes System beteiligt ist (z. B. wenn Sie den State einer Komponente aktualisieren möchten, wenn sich einige Props oder der State ändern), sollten Sie keinen Effect benötigen. Das Entfernen unnötiger Effects macht Ihren Code leichter verständlich, schneller in der Ausführung und weniger fehleranfällig.

Sie werden lernen
  • Warum und wie Sie unnötige Effects aus Ihren Komponenten entfernen
  • Wie Sie aufwändige Berechnungen ohne Effects zwischenspeichern
  • Wie Sie den Komponenten-State ohne Effects zurücksetzen und anpassen
  • Wie Sie Logik zwischen Event-Handlern teilen
  • Welche Logik in Event-Handler verschoben werden sollte
  • Wie Sie übergeordnete Komponenten über Änderungen benachrichtigen

Wie Sie unnötige Effects entfernen

Es gibt zwei häufige Fälle, in denen Sie keine Effects benötigen:

  • Sie benötigen keine Effects, um Daten für das Rendering zu transformieren.Angenommen, Sie möchten eine Liste filtern, bevor Sie sie anzeigen. Sie könnten versucht sein, einen Effect zu schreiben, der eine State-Variable aktualisiert, wenn sich die Liste ändert. Dies ist jedoch ineffizient. Wenn Sie den State aktualisieren, ruft React zunächst Ihre Komponentenfunktionen auf, um zu berechnen, was auf dem Bildschirm angezeigt werden soll. Dann wird React diese Änderungen an das DOM"committen", um den Bildschirm zu aktualisieren. Danach führt React Ihre Effects aus. Wenn Ihr Effectauchsofort den State aktualisiert, beginnt der gesamte Prozess von vorne! Um unnötige Render-Durchläufe zu vermeiden, transformieren Sie alle Daten auf der obersten Ebene Ihrer Komponenten. Dieser Code wird automatisch neu ausgeführt, wenn sich Ihre Props oder Ihr State ändern.
  • Sie benötigen keine Effects, um Benutzerereignisse zu behandeln.Angenommen, Sie möchten eine/api/buyPOST-Anfrage senden und eine Benachrichtigung anzeigen, wenn der Benutzer ein Produkt kauft. Im Klick-Event-Handler der Kauf-Schaltfläche wissen Sie genau, was passiert ist. Wenn ein Effect ausgeführt wird, wissen Sie nicht mehr,wasder Benutzer getan hat (z. B. welche Schaltfläche geklickt wurde). Deshalb behandeln Sie Benutzerereignisse normalerweise in den entsprechenden Event-Handlern.

SiebenötigenEffects, um sich mit externen Systemen zusynchronisieren. Sie können beispielsweise einen Effect schreiben, der ein jQuery-Widget mit dem React-State synchron hält. Sie können auch Daten mit Effects abrufen: Beispielsweise können Sie die Suchergebnisse mit der aktuellen Suchanfrage synchronisieren. Beachten Sie, dass moderneFrameworkseffizientere eingebaute Datenabrufmechanismen bieten, als Effects direkt in Ihren Komponenten zu schreiben.

Um Ihnen die richtige Intuition zu vermitteln, sehen wir uns einige gängige konkrete Beispiele an!

Aktualisieren des States basierend auf Props oder State

Angenommen, Sie haben eine Komponente mit zwei Zustandsvariablen:firstNameundlastName. Sie möchten einenfullNamedaraus berechnen, indem Sie sie verketten. Außerdem soll sichfullNameaktualisieren, wenn sichfirstNameoderlastNameändern. Ihr erster Impuls könnte sein, einefullName-Zustandsvariable hinzuzufügen und sie in einem Effekt zu aktualisieren:

Das ist komplizierter als nötig. Es ist auch ineffizient: Es führt einen gesamten Render-Durchlauf mit einem veralteten Wert fürfullNamedurch und rendert dann sofort mit dem aktualisierten Wert neu. Entfernen Sie die Zustandsvariable und den Effekt:

Wenn etwas aus den vorhandenen Props oder dem Zustand berechnet werden kann,legen Sie es nicht in den Zustand.Berechnen Sie es stattdessen während des Renderns.Dadurch wird Ihr Code schneller (Sie vermeiden die zusätzlichen "kaskadierenden" Updates), einfacher (Sie entfernen Code) und weniger fehleranfällig (Sie vermeiden Fehler, die dadurch entstehen, dass verschiedene Zustandsvariablen nicht synchron zueinander sind). Wenn dieser Ansatz neu für Sie ist, erklärtThinking in React, was in den Zustand gehört.

Aufwändige Berechnungen zwischenspeichern

Diese Komponente berechnetvisibleTodos, indem sie dietodos, die sie per Props erhält, gemäß demfilter-Prop filtert. Sie könnten versucht sein, das Ergebnis im Zustand zu speichern und es über einen Effekt zu aktualisieren:

Wie im vorherigen Beispiel ist dies unnötig und ineffizient. Entfernen Sie zunächst den State und den Effekt:

Normalerweise ist dieser Code in Ordnung! Aber vielleicht istgetFilteredTodos()langsam oder Sie haben vieletodos. In diesem Fall möchten Sie nicht, dassgetFilteredTodos()neu berechnet wird, wenn sich eine unabhängige State-Variable wienewTodogeändert hat.

Sie können eine aufwändige Berechnung zwischenspeichern (oder„memoisieren“), indem Sie sie in einenuseMemoHook einwickeln:

Hinweis

React Compilerkann aufwändige Berechnungen automatisch für Sie memoieren, sodass in vielen Fällen kein manuellesuseMemomehr nötig ist.

Oder als einzelne Zeile geschrieben:

Dies teilt React mit, dass die innere Funktion nicht erneut ausgeführt werden soll, es sei denn,todosoderfilterhaben sich geändert.React merkt sich den Rückgabewert vongetFilteredTodos()während des ersten Renders. Bei den nächsten Renders prüft es, ob sichtodosoderfiltergeändert haben. Wenn sie gleich wie beim letzten Mal sind, gibtuseMemodas zuletzt gespeicherte Ergebnis zurück. Wenn sie sich unterscheiden, ruft React die innere Funktion erneut auf (und speichert ihr Ergebnis).

Die Funktion, die Sie inuseMemoeinwickeln, wird während des Renderns ausgeführt, daher funktioniert dies nur fürreine Berechnungen.

Deep Dive
Wie erkennt man, ob eine Berechnung aufwändig ist?

Alle States zurücksetzen, wenn sich eine Prop ändert

Die KomponenteProfilePageerhält eineuserIdProp. Die Seite enthält ein Kommentarfeld, und Sie verwenden einecommentState-Variable, um ihren Wert zu halten. Eines Tages bemerken Sie ein Problem: Wenn Sie von einem Profil zu einem anderen navigieren, wird dercommentState nicht zurückgesetzt. Dadurch kann versehentlich leicht ein Kommentar unter dem falschen Profil gepostet werden. Um

Dies ist ineffizient, weilProfilePageund seine Kinder zunächst mit dem veralteten Wert rendern und dann erneut rendern. Es ist auch kompliziert, weil Sie dies injederKomponente tun müssten, die einen Zustand innerhalb vonProfilePagehat. Wenn beispielsweise die Kommentar-UI verschachtelt ist, sollten Sie auch den verschachtelten Kommentarzustand löschen.

Stattdessen können Sie React mitteilen, dass jedes Benutzerprofil konzeptionell einanderesProfil ist, indem Sie ihm einen expliziten Schlüssel geben. Teilen Sie Ihre Komponente in zwei Teile auf und übergeben Sie einkey-Attribut von der äußeren Komponente an die innere:

Normalerweise bewahrt React den Zustand, wenn dieselbe Komponente an derselben Stelle gerendert wird.Indem SieuserIdalskeyan dieProfile-Komponente übergeben, bitten Sie React, zweiProfile-Komponenten mit unterschiedlichenuserIdals zwei verschiedene Komponenten zu behandeln, die keinen Zustand teilen sollten.Immer wenn sich der Schlüssel (den Sie aufuserIdgesetzt haben) ändert, wird React das DOM neu erstellen undden Zustand zurücksetzenderProfile-Komponente und all ihrer Kinder. Jetzt wird dascomment-Feld automatisch gelöscht, wenn zwischen Profilen navigiert wird.

Beachten Sie, dass in diesem Beispiel nur die äußereProfilePage-Komponente exportiert wird und für andere Dateien im Projekt sichtbar ist. Komponenten, dieProfilePagerendern, müssen ihr keinen Schlüssel übergeben: Sie übergebenuserIdals reguläre Prop. Die Tatsache, dassProfilePagesie alskeyan die innereProfile-Komponente weitergibt, ist ein Implementierungsdetail.

Anpassen eines Teils des Zustands, wenn sich eine Prop ändert

Manchmal möchten Sie einen Teil des Zustands bei einer Prop-Änderung zurücksetzen oder anpassen, aber nicht den gesamten.

DieseList-Komponente erhält eine Liste vonitemsals Prop und verwaltet das ausgewählte Element in der Zustandsvariableselection. Sie möchtenselectionaufnullzurücksetzen, wann immer das Propitemsein anderes Array erhält:

Auch das ist nicht ideal. Jedes Mal, wenn sichitemsändert, werdenListund seine Kindkomponenten zunächst mit einem veraltetenselection-Wert gerendert. Dann aktualisiert React das DOM und führt die Effects aus. Schließlich führt der Aufruf vonsetSelection(null)zu einem erneuten Rendering vonListund seinen Kindkomponenten, wodurch dieser gesamte Prozess von neuem beginnt.

Beginnen Sie damit, den Effect zu löschen. Passen Sie den Zustand stattdessen direkt während des Renderings an:

Informationen aus vorherigen Renderings speichernwie diese kann schwer zu verstehen sein, aber es ist besser, als denselben Zustand in einem Effect zu aktualisieren. Im obigen Beispiel wirdsetSelectiondirekt während eines Renderings aufgerufen. React wird dieListsofortnach dem Verlassen mit einerreturn-Anweisung erneut rendern. React hat die Kinder vonListnoch nicht gerendert oder das DOM aktualisiert, sodass dies den Kindern vonListermöglicht, das Rendering des veraltetenselection-Werts zu überspringen.

Wenn Sie eine Komponente während des Renderings aktualisieren, verwirft React das zurückgegebene JSX und versucht sofort, erneut zu rendern. Um sehr langsame kaskadierende Wiederholungsversuche zu vermeiden, lässt React Sie nur den ZustandderselbenKomponente während eines Renderings aktualisieren. Wenn Sie den Zustand einer anderen Komponente während eines Renderings aktualisieren, erhalten Sie eine Fehlermeldung. Eine Bedingung wieitems !== prevItemsist notwendig, um Schleifen zu vermeiden. Sie können den Zustand auf diese Weise anpassen, aber alle anderen Nebeneffekte (wie das Ändern des DOM oder das Setzen von Timeouts) sollten in Event-Handlern oder Effects bleiben, umKomponenten rein zu halten.

Obwohl dieses Muster effizienter ist als ein Effekt, sollten die meisten Komponenten es ebenfalls nicht benötigen.Egal wie Sie es tun, das Anpassen des Zustands basierend auf Props oder anderen Zuständen macht Ihren Datenfluss schwieriger zu verstehen und zu debuggen. Prüfen Sie immer, ob Sieden gesamten Zustand mit einem Schlüssel zurücksetzenoderalles während des Renderns berechnenkönnen. Speichern Sie beispielsweise nicht das ausgewählteElement(und setzen es zurück), sondern speichern Sie die ausgewählteElement-ID:

Jetzt muss der State überhaupt nicht mehr "angepasst" werden. Wenn sich das Element mit der ausgewählten ID in der Liste befindet, bleibt es ausgewählt. Wenn nicht, wird dieselection, die während des Renderns berechnet wird,nullsein, da kein passendes Element gefunden wurde. Dieses Verhalten ist anders, aber wohl besser, da die meisten Änderungen anitemsdie Auswahl beibehalten.

Logik zwischen Event-Handlern teilen

Angenommen, Sie haben eine Produktseite mit zwei Buttons (Kaufen und Zur Kasse), die beide das Produkt kaufen lassen. Sie möchten eine Benachrichtigung anzeigen, wann immer der Benutzer das Produkt in den Warenkorb legt. Den Aufruf vonshowNotification()in den Klick-Handlern beider Buttons zu wiederholen, fühlt sich redundant an, daher könnten Sie versucht sein, diese Logik in einen Effect zu legen:

Dieser Effect ist unnötig. Er wird höchstwahrscheinlich auch Bugs verursachen. Nehmen wir zum Beispiel an, Ihre App "merkt" sich den Warenkorb zwischen Seitenneuladungen. Wenn Sie ein Produkt einmal in den Warenkorb legen und die Seite aktualisieren, erscheint die Benachrichtigung erneut. Sie wird jedes Mal erscheinen, wenn Sie die Seite des Produkts aktualisieren. Das liegt daran, dassproduct.isInCartbeim Laden der Seite bereitstruesein wird, sodass der obige EffectshowNotification()aufruft.

Wenn Sie unsicher sind, ob Code in einem Effect oder einem Event-Handler stehen sollte, fragen Sie sich,warumdieser Code ausgeführt werden muss. Verwenden Sie Effects nur für Code, der ausgeführt werden sollte,weildie Komponente dem Benutzer angezeigt wurde.In diesem Beispiel sollte die Benachrichtigung erscheinen, weil der Benutzerden Button gedrückt hat, nicht weil die Seite angezeigt wurde! Löschen Sie den Effect und platzieren Sie die gemeinsame Logik in einer Funktion, die von beiden Event-Handlern aufgerufen wird:

Dies entfernt sowohl den unnötigen Effect als auch behebt den Bug.

Eine POST-Anfrage senden

DieseForm-Komponente sendet zwei Arten von POST-Anfragen. Sie sendet ein Analytics-Event, wenn sie gemountet wird. Wenn Sie das Formular ausfüllen und auf den Submit-Button klicken, sendet sie eine POST-Anfrage an den/api/register-Endpunkt:

Wenden wir die gleichen Kriterien wie im vorherigen Beispiel an.

Die Analytics-POST-Anfrage sollte in einem Effect bleiben. DerGrundfür das Senden des Analytics-Events ist, dass das Formular angezeigt wurde. (Es würde in der Entwicklung zweimal ausgelöst werden, abersiehe hier, wie man damit umgeht.)

Die /api/registerPOST-Anfrage wird jedoch nicht dadurch verursacht, dass das Formularangezeigtwird. Sie möchten die Anfrage nur zu einem bestimmten Zeitpunkt senden: wenn der Benutzer den Button drückt. Es sollte nurbei dieser speziellen Interaktiongeschehen. Löschen Sie den zweiten Effect und verschieben Sie die POST-Anfrage in den Event-Handler:

Wenn Sie entscheiden, ob Sie eine Logik in einen Event-Handler oder einen Effect setzen, ist die Hauptfrage, die Sie beantworten müssen,welche Art von Logikes aus Benutzersicht ist. Wenn diese Logik durch eine bestimmte Interaktion verursacht wird, behalten Sie sie im Event-Handler. Wenn sie dadurch verursacht wird, dass der Benutzer die Komponente auf dem Bildschirmsieht, behalten Sie sie im Effect.

Berechnungsketten

Manchmal könnten Sie versucht sein, Effects zu verketten, die jeweils ein Stück State basierend auf anderem State anpassen:

Dieser Code hat zwei Probleme.

Das erste Problem ist, dass er sehr ineffizient ist: Die Komponente (und ihre Kinder) müssen zwischen jedemset-Aufruf in der Kette neu gerendert werden. Im obigen Beispiel gibt es im schlimmsten Fall (setCard→ Rendern →setGoldCardCount→ Rendern →setRound→ Rendern →setIsGameOver→ Rendern) drei unnötige Neu-Renderings des darunterliegenden Baums.

Das zweite Problem ist, dass selbst wenn es nicht langsam wäre, Sie beim Weiterentwickeln Ihres Codes auf Fälle stoßen werden, in denen die von Ihnen geschriebene "Kette" nicht zu den neuen Anforderungen passt. Stellen Sie sich vor, Sie fügen eine Möglichkeit hinzu, den Spielverlauf schrittweise durchzugehen. Sie würden dies tun, indem Sie jede Zustandsvariable auf einen Wert aus der Vergangenheit setzen. Das Setzen descard-Zustands auf einen Wert aus der Vergangenheit würde jedoch die Effektkette erneut auslösen und die angezeigten Daten verändern. Solcher Code ist oft starr und fragil.

In diesem Fall ist es besser, Berechnungen während des Renderings durchzuführen und den Zustand im Event-Handler anzupassen:

Dies ist wesentlich effizienter. Außerdem können Sie, wenn Sie eine Möglichkeit zum Anzeigen des Spielverlaufs implementieren, nun jede Zustandsvariable auf einen Zug aus der Vergangenheit setzen, ohne die Effektkette auszulösen, die jeden anderen Wert anpasst. Wenn Sie Logik zwischen mehreren Event-Handlern wiederverwenden müssen, können Sieeine Funktion extrahierenund sie von diesen Handlern aufrufen.

Denken Sie daran, dass sich der Zustand innerhalb von Event-Handlernwie eine Momentaufnahme verhält.Beispielsweise spiegelt die Variableroundauch nach dem Aufruf vonsetRound(round + 1)den Wert zum Zeitpunkt des Klicks auf die Schaltfläche wider. Wenn Sie den nächsten Wert für Berechnungen benötigen, definieren Sie ihn manuell, z.B. alsconst nextRound = round + 1.

In einigen FällenkönnenSie den nächsten Zustand nicht direkt im Event-Handler berechnen. Stellen Sie sich beispielsweise ein Formular mit mehreren Dropdown-Menüs vor, bei dem die Optionen des nächsten Menüs vom ausgewählten Wert des vorherigen abhängen. Dann ist eine Effektkette angebracht, da Sie sich mit dem Netzwerk synchronisieren.

Initialisierung der Anwendung

Einige Logik sollte nur einmal ausgeführt werden, wenn die App geladen wird.

Sie könnten versucht sein, sie in einen Effekt in der obersten Komponente zu setzen:

Sie werden jedoch schnell feststellen, dass siein der Entwicklung zweimal ausgeführt wird.Dies kann Probleme verursachen – vielleicht wird beispielsweise das Authentifizierungs-Token ungültig, weil die Funktion nicht dafür ausgelegt ist, zweimal aufgerufen zu werden. Im Allgemeinen sollten Ihre Komponenten robust gegenüber einem erneuten Einhängen sein. Dies schließt Ihre obersteApp-Komponente ein.

Auch wenn sie in der Produktion in der Praxis möglicherweise nie erneut eingehängt wird, erleichtert die Einhaltung derselben Einschränkungen in allen Komponenten das Verschieben und Wiederverwenden von Code. Wenn eine Logikeinmal pro App-Ladungund nichteinmal pro Komponenten-Einhängungausgeführt werden muss, fügen Sie eine Variable auf oberster Ebene hinzu, um zu verfolgen, ob sie bereits ausgeführt wurde:

Sie können sie auch während der Modulinitialisierung und vor dem Rendern der App ausführen:

Code auf oberster Ebene wird einmal ausgeführt, wenn Ihre Komponente importiert wird – selbst wenn sie letztendlich nicht gerendert wird. Um Verlangsamungen oder überraschendes Verhalten beim Import beliebiger Komponenten zu vermeiden, verwenden Sie dieses Muster nicht übermäßig. Beschränken Sie anwendungsweite Initialisierungslogik auf Root-Komponentenmodule wieApp.jsoder auf den Einstiegspunkt Ihrer Anwendung.

Über Zustandsänderungen an übergeordnete Komponenten benachrichtigen

Angenommen, Sie schreiben eineToggle-Komponente mit einem internen ZustandisOn, der entwedertrueoderfalsesein kann. Es gibt verschiedene Möglichkeiten, ihn umzuschalten (durch Klicken

Wie zuvor ist dies nicht ideal. Die KomponenteToggleaktualisiert zuerst ihren Zustand, und React aktualisiert den Bildschirm. Dann führt React den Effekt aus, der die von einer Elternkomponente übergebene FunktiononChangeaufruft. Nun wird die Elternkomponente ihren eigenen Zustand aktualisieren, was einen weiteren Render-Durchlauf startet. Es wäre besser, alles in einem einzigen Durchlauf zu erledigen.

Lösche den Effekt und aktualisiere stattdessen den ZustandbeiderKomponenten innerhalb desselben Event-Handlers:

Mit diesem Ansatz aktualisieren sowohl dieToggle-Komponente als auch ihre Elternkomponente ihren Zustand während des Events. Reactbündelt Updatesvon verschiedenen Komponenten zusammen, sodass es nur einen Render-Durchlauf geben wird.

Möglicherweise kannst du den Zustand auch vollständig entfernen und stattdessenisOnvon der Elternkomponente empfangen:

„State nach oben heben“ermöglicht es der Elternkomponente, dieTogglevollständig zu steuern, indem sie ihren eigenen Zustand umschaltet. Das bedeutet, dass die Elternkomponente mehr Logik enthalten muss, aber insgesamt gibt es weniger Zustand, um den man sich kümmern muss. Immer wenn du versuchst, zwei verschiedene Zustandsvariablen zu synchronisieren, versuche stattdessen, den State nach oben zu heben!

Daten an die Elternkomponente übergeben

DieseChild-Komponente holt einige Daten und übergibt sie dann in einem Effekt an dieParent-Komponente:

In React fließen Daten von den Elternkomponenten zu ihren Kindern. Wenn du etwas Falsches auf dem Bildschirm siehst, kannst du nachverfolgen, woher die Information kommt, indem du die Komponentenhierarchie nach oben gehst, bis du die Komponente findest, die den falschen Prop übergibt oder den falschen Zustand hat. Wenn Kindkomponenten den Zustand ihrer Elternkomponenten in Effekten aktualisieren, wird der Datenfluss sehr schwer nachzuvollziehen. Da sowohl das Kind als auch die Eltern dieselben Daten benötigen, lass die Elternkomponente diese Daten abrufen undgib sie an das Kind weiter:

Dies ist einfacher und hält den Datenfluss vorhersehbar: Die Daten fließen von der Elternkomponente zur Kindkomponente.

Auf einen externen Store abonnieren

Manchmal müssen deine Komponenten möglicherweise Daten außerhalb des React-Zustands abonnieren. Diese Daten könnten von einer Drittanbieter-Bibliothek oder einer eingebauten Browser-API stammen. Da sich diese Daten ohne das Wissen von React ändern können, musst du deine Komponenten manuell darauf abonnieren. Dies wird oft mit einem Effekt gemacht, zum Beispiel:

Hier abonniert die Komponente einen externen Datenspeicher (in diesem Fall die Browser-APInavigator.onLine). Da diese API auf dem Server nicht existiert (und daher nicht für das initiale HTML verwendet werden kann), wird der Zustand zunächst auftruegesetzt. Immer wenn sich der Wert dieses Datenspeichers im Browser ändert, aktualisiert die Komponente ihren Zustand.

Obwohl es üblich ist, Effekte dafür zu verwenden, hat React einen speziell dafür vorgesehenen Hook zum Abonnieren eines externen Stores, der stattdessen bevorzugt wird. Lösche den Effekt und ersetze ihn durch einen Aufruf vonuseSyncExternalStore:

Dieser Ansatz ist weniger fehleranfällig als das manuelle Synchronisieren veränderlicher Daten mit dem React-Zustand über einen Effekt. Typischerweise schreibst du einen benutzerdefinierten Hook wieuseOnlineStatus()oben, damit du diesen Code nicht in den einzelnen Komponenten wiederholen musst.Erfahre mehr über das Abonnieren externer Stores von React-Komponenten.

Daten abrufen

Viele Apps verwenden Effekte, um das Abrufen von Daten zu starten. Es ist ziemlich üblich, einen Datenabruf-Effekt wie diesen zu schreiben:

Siemüssendiesen Fetch nicht in einen Event-Handler verschieben.

Dies mag wie ein Widerspruch zu den früheren Beispielen erscheinen, in denen Sie die Logik in Event-Handler einfügen mussten! Bedenken Sie jedoch, dass nichtdas Tippen-Ereignisder Hauptgrund für den Fetch ist. Suchfelder werden oft von der URL vorausgefüllt, und der Benutzer könnte über die Zurück- und Vorwärts-Navigation wechseln, ohne das Eingabefeld zu berühren.

Es spielt keine Rolle, woherpageundquerystammen. Solange diese Komponente sichtbar ist, sollten Sie dieresultssynchronisiertmit den Daten aus dem Netzwerk für die aktuellepageundqueryhalten. Deshalb handelt es sich um einen Effekt.

Der obige Code hat jedoch einen Fehler. Stellen Sie sich vor, Sie tippen schnell"hello". Dann ändert sich diequery von "h"zu"he","hel","hell"und"hello". Dies löst separate Fetches aus, aber es gibt keine Garantie, in welcher Reihenfolge die Antworten eintreffen. Zum Beispiel könnte die Antwort für"hell" nachder Antwort für"hello"eintreffen. Da sie zuletztsetResults()aufrufen würde, würden Sie die falschen Suchergebnisse anzeigen. Dies wird als„Race Condition“bezeichnet: zwei verschiedene Anfragen „konkurrierten“ miteinander und kamen in einer anderen Reihenfolge an als erwartet.

Um die Race Condition zu beheben, müssen Sieeine Cleanup-Funktion hinzufügen, um veraltete Antworten zu ignorieren:

Dies stellt sicher, dass bei einem Daten-Fetch durch Ihren Effekt alle Antworten außer der zuletzt angeforderten ignoriert werden.

Die Behandlung von Race Conditions ist nicht die einzige Schwierigkeit bei der Implementierung von Daten-Fetching. Sie sollten auch über das Caching von Antworten nachdenken (damit der Benutzer auf Zurück klicken und den vorherigen Bildschirm sofort sehen kann), wie Daten auf dem Server abgerufen werden (damit das initial serverseitig gerenderte HTML den abgerufenen Inhalt enthält und keinen Ladeindikator) und wie Netzwerk-Wasserfälle vermieden werden können (damit eine untergeordnete Komponente Daten abrufen kann, ohne auf jede übergeordnete Komponente zu warten).

Diese Probleme gelten für jede UI-Bibliothek, nicht nur für React. Ihre Lösung ist nicht trivial, weshalb moderneFrameworkseffizientere eingebaute Datenabrufmechanismen bieten, als das Abrufen von Daten in Effekten.

Wenn Sie kein Framework verwenden (und auch kein eigenes bauen möchten), aber das Daten-Fetching aus Effekten ergonomischer gestalten möchten, ziehen Sie in Betracht, Ihre Fetch-Logik in einen benutzerdefinierten Hook zu extrahieren, wie in diesem Beispiel:

Sie möchten wahrscheinlich auch Logik für die Fehlerbehandlung und zur Verfolgung, ob Inhalte geladen werden, hinzufügen. Sie können einen solchen Hook selbst bauen oder eine der vielen bereits in der React-Ökosystem verfügbaren Lösungen verwenden.Auch wenn dies allein nicht so effizient ist wie die Verwendung des eingebauten Datenabrufmechanismus eines Frameworks, erleichtert das Verschieben der Datenabruflogik in einen benutzerdefinierten Hook die spätere Übernahme einer effizienten Datenabrufstrategie.

Im Allgemeinen sollten Sie, wann immer Sie auf das Schreiben von Effekten zurückgreifen müssen, darauf achten, wann Sie ein Stück Funktionalität in einen benutzerdefinierten Hook mit einer deklarativeren und zielgerichteteren API extrahieren können, wieuseDataoben. Je weniger roheuseEffect-Aufrufe Sie in Ihren Komponenten haben, desto einfacher wird die Wartung Ihrer Anwendung.

Zusammenfassung

  • Wenn du etwas während des Renderns berechnen kannst, brauchst du keinen Effekt.
  • Um aufwändige Berechnungen zu cachen, fügeuseMemoanstelle vonuseEffecthinzu.
  • Um den State eines gesamten Komponentenbaums zurückzusetzen, übergebe ihm einen anderenkey.
  • Um einen bestimmten State-Teil als Reaktion auf eine Prop-Änderung zurückzusetzen, setze ihn während des Renderns.
  • Code, der läuft, weil eine Komponenteangezeigtwurde, sollte in Effekten sein, der Rest sollte in Events sein.
  • Wenn du den State mehrerer Komponenten aktualisieren musst, ist es besser, dies in einem einzigen Event zu tun.
  • Wenn du versuchst, State-Variablen in verschiedenen Komponenten zu synchronisieren, ziehe in Betracht, den State nach oben zu heben.
  • Du kannst Daten mit Effekten abrufen, musst aber eine Cleanup-Funktion implementieren, um Race Conditions zu vermeiden.

Try out some challenges

Challenge 1 of 4:Transform data without Effects #

The TodoList below displays a list of todos. When the “Show only active todos” checkbox is ticked, completed todos are not displayed in the list. Regardless of which todos are visible, the footer displays the count of todos that are not yet completed.

Simplify this component by removing all the unnecessary state and Effects.