TL; DR
Wie kann eine übergeordnete Komponente wissen, wann das Rendern jeder untergeordneten Komponente abgeschlossen ist und das DOM für den Benutzer mit seiner aktuellsten Version sichtbar ist?
Angenommen, ich habe eine component A
untergeordnete Grid
Komponente, die aus 3x3
Enkelkomponenten besteht. Jede dieser Enkelkomponenten ruft Daten von einem erholsamen API-Endpunkt ab und rendert sich selbst, wenn die Daten verfügbar werden.
Ich möchte den gesamten Bereich von Component A
mit einem loader
Platzhalter abdecken, der erst dann angezeigt wird, wenn die letzte Komponente im Raster die Daten erfolgreich abgerufen und gerendert hat, sodass sie sich bereits im DOM befinden und angezeigt werden können.
Die Benutzererfahrung sollte ein super reibungsloser Übergang vom "Lader" zu einem vollständig bestückten Raster sein, ohne zu flackern.
Mein Problem ist, genau zu wissen, wann die Komponenten unter dem Lader enthüllt werden müssen.
Gibt es einen Mechanismus, auf den ich mich verlassen kann, um dies mit absoluter Genauigkeit zu tun? Ich muss kein Zeitlimit für den Loader fest codieren. Soweit ich ComponentDidMount
weiß, ist es auch unzuverlässig , sich auf jedes Kind zu verlassen, da dies nicht garantiert, dass die Komponente zum Zeitpunkt des Anrufs für den Benutzer vollständig sichtbar ist.
Um die Frage noch weiter zu vertiefen:
Ich habe eine Komponente, die Daten rendert. Nachdem es initialisiert wurde, hat es es nicht, also
componentDidMount
trifft es einen API-Endpunkt dafür. Sobald die Daten empfangen wurden, ändert sich ihr Status, um sie wiederzugeben. Dies führt verständlicherweise zu einem erneuten Rendern des Endzustands dieser Komponente. Meine Frage lautet: Woher weiß ich, wann dieses erneute Rendern stattgefunden hat und sich im DOM des Benutzers widerspiegelt ? Dieser Zeitpunkt! = Der Zeitpunkt, zu dem sich der Status der Komponente geändert hat, um Daten zu enthalten.
ComponentDidMount
ichaxios
zum Abrufen von Daten. Wenn die Daten eingehen, ändere ich den Status dieser Komponente, wodurch die Daten gerendert werden. Theoretisch können 8Antworten:
In React gibt es zwei Lifecycle-Hooks, die aufgerufen werden, nachdem das DOM einer Komponente gerendert wurde:
Für Ihren Anwendungsfall ist Ihre übergeordnete Komponente P interessiert, wenn N untergeordnete Komponenten jeweils eine Bedingung X erfüllt haben . X kann als Sequenz definiert werden:
Durch Kombinieren des Status der Komponente und Verwenden des
componentDidUpdate
Hooks können Sie feststellen, wann die Sequenz abgeschlossen ist und Ihre Komponente die Bedingung X erfüllt.Sie können verfolgen, wann Ihre asynchrone Operation abgeschlossen ist, indem Sie eine Statusvariable festlegen. Zum Beispiel:
Nach dem Einstellen des Status ruft React die
componentDidUpdate
Funktion Ihrer Komponenten auf . Durch Vergleichen der aktuellen und vorherigen Statusobjekte in dieser Funktion können Sie der übergeordneten Komponente signalisieren, dass Ihre asynchrone Operation abgeschlossen und der Status Ihrer neuen Komponente gerendert wurde:In Ihrer P-Komponente können Sie einen Zähler verwenden, um zu verfolgen, wie viele Kinder sinnvoll aktualisiert wurden:
Wenn Sie die Länge von N kennen , können Sie schließlich wissen, wann alle sinnvollen Aktualisierungen stattgefunden haben, und in Ihrer Rendermethode von P entsprechend handeln :
quelle
Ich würde es so einrichten, dass Sie sich auf eine globale Statusvariable verlassen, um Ihren Komponenten mitzuteilen, wann sie gerendert werden sollen. Redux ist besser für dieses Szenario, in dem viele Komponenten miteinander sprechen, und Sie haben in einem Kommentar erwähnt, dass Sie es manchmal verwenden. Also skizziere ich eine Antwort mit Redux.
Sie müssten Ihre API-Aufrufe in den übergeordneten Container verschieben.
Component A
. Wenn Sie möchten, dass Ihre Enkelkinder erst nach Abschluss der API-Aufrufe gerendert werden, können Sie diese API-Aufrufe nicht in den Enkelkindern selbst behalten. Wie kann ein API-Aufruf von einer Komponente aus erfolgen, die noch nicht vorhanden ist?Sobald alle API-Aufrufe ausgeführt wurden, können Sie mithilfe von Aktionen eine globale Statusvariable aktualisieren, die eine Reihe von Datenobjekten enthält. Jedes Mal, wenn Daten empfangen werden (oder ein Fehler abgefangen wird), können Sie eine Aktion auslösen, um zu überprüfen, ob Ihr Datenobjekt vollständig ausgefüllt ist. Sobald es vollständig ausgefüllt ist, können Sie eine
loading
Variable auf aktualisierenfalse
und Ihre unter bestimmten Bedingungen rendernGrid
Komponente .Also zum Beispiel:
Ihr Reduzierer hält das globale Statusobjekt bereit, das angibt, ob alles bereit ist oder nicht:
In Ihren Handlungen:
Beiseite
Wenn Sie wirklich mit der Idee verheiratet sind, dass Ihre API-Aufrufe in jedem Enkelkind gespeichert sind, das gesamte Raster der Enkelkinder jedoch erst nach Abschluss aller API-Aufrufe gerendert wird, müssen Sie eine völlig andere Lösung verwenden. In diesem Fall müssten Ihre Enkelkinder von Anfang an gerendert werden, um ihre Aufrufe zu tätigen, haben jedoch eine CSS-Klasse mit
display: none
, die sich erst ändert, nachdem die globale Statusvariableloading
als falsch markiert wurde. Dies ist auch machbar, aber irgendwie neben dem Punkt der Reaktion.quelle
Sie können dies möglicherweise mit React Suspense lösen.
Die Einschränkung ist, dass es keine gute Idee ist, über den Komponentenbaum hinaus anzuhalten, der das Rendern ausführt (dh: Wenn Ihre Komponente einen Renderprozess startet, ist es meiner Erfahrung nach keine gute Idee, diese Komponente anzuhalten) Wahrscheinlich ist es eine bessere Idee, die Anforderungen in der Komponente zu starten, die die Zellen rendert . Etwas wie das:
Wie Suspense sich mit Redux paart, weiß ich nicht genau. Die ganze Idee hinter dieser (experimentellen!) Version von Suspense ist, dass Sie den Abruf sofort während des Renderzyklus einer übergeordneten Komponente starten und ein Objekt übergeben, das einen Abruf an die untergeordneten Komponenten darstellt. Dies verhindert, dass Sie eine Art Barrier-Objekt benötigen (das Sie bei anderen Ansätzen benötigen würden).
Ich werde sagen, dass ich nicht denke, dass es der richtige Ansatz ist , zu warten, bis alles abgerufen wurde, um etwas anzuzeigen , da dann die Benutzeroberfläche so langsam ist wie die langsamste Verbindung oder möglicherweise überhaupt nicht funktioniert!
Hier ist der Rest des fehlenden Codes:
Und eine Sandbox: https://codesandbox.io/s/falling-dust-b8e7s
quelle
Erstens könnte der Lebenszyklus eine Async / Wait-Methode haben.
Was wir wissen müssen
componentDidMount
undcomponentWillMount
,Meiner Meinung nach wäre es gut genug, sie einfach mit einem normalen Lebenszyklus zu implementieren.
Material-UI CircularProgress
Da das erneute Rendern des Kindes dazu führt, dass die Eltern dasselbe tun, ist es in
didUpdate
Ordnung , es einzufangen .Und da es nach dem Rendern aufgerufen wird, sollten die Seiten danach nicht mehr geändert werden, wenn Sie die Überprüfungsfunktion als Ihre Anforderung festlegen.
Wir verwenden diese Implementierung in unserem Produkt, das eine API hat, die 30s veranlasst, die Antwort zu erhalten. Soweit ich sehe, hat alles gut funktioniert.
quelle
Während Sie einen
componentDidMount
API-Aufruf tätigen, werden die Daten beim Auflösen des API-Aufrufs gespeichert,componentDidUpdate
während dieser Lebenszyklus SieprevProps
undprevState
. Sie können also Folgendes tun:Wenn Sie dies wissen möchten, bevor die Komponente erneut gerendert wird, können Sie verwenden
getSnapshotBeforeUpdate
.https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate .
quelle
Es gibt mehrere Ansätze, mit denen Sie gehen können:
quelle