VollstÀndiges UseEffect-Handbuch

Sie haben mehrere Komponenten mit Hooks geschrieben . Vielleicht - sie haben sogar eine kleine Anwendung erstellt. Im Allgemeinen sind Sie mit dem Ergebnis sehr zufrieden. Sie sind an die API gewöhnt und haben dabei einige unbekannte nĂŒtzliche Tricks gefunden. Sie haben sogar einige Ihrer eigenen Hooks erstellt und Ihren Code auf 300 Zeilen reduziert, indem Sie das eingefĂŒgt haben, was zuvor durch Wiederholen von Programmfragmenten dargestellt wurde. Was Sie getan haben, haben Sie Kollegen gezeigt. "Gut gemacht", sagten sie ĂŒber Ihr Projekt.


Aber manchmal, wenn Sie useEffect verwenden, useEffect die Komponenten der Softwaremechanismen nicht sehr gut zusammen. Es scheint dir, dass dir etwas fehlt. All dies Àhnelt der Arbeit mit klassenbasierten Komponentenlebenszyklusereignissen ... aber ist es wirklich so?
Wenn Sie versuchen zu verstehen, was genau nicht zu Ihnen passt, stellen Sie fest, dass Sie die folgenden Fragen stellen:

  • Wie spiele useEffect componentDidMount mit useEffect ?
  • Wie lade useEffect Daten in useEffect ? Was ist [] ?
  • MĂŒssen Funktionen als EffektabhĂ€ngigkeiten angegeben werden?
  • Warum gerĂ€t ein Programm manchmal in eine Endlosschleife zum erneuten Laden von Daten?
  • Warum ist der alte Zustand manchmal in den Effekten sichtbar oder werden alte Eigenschaften gefunden?

Als ich anfing, Haken zu benutzen, quĂ€lten mich diese Fragen auch. Selbst als ich die Dokumentation vorbereitete, konnte ich nicht sagen, dass ich einige Feinheiten perfekt beherrsche. Seitdem hatte ich einige Momente, in denen ich plötzlich etwas Wichtiges erkannte und wirklich ausrufen wollte: "Eureka!" Über das, was ich in diesen Momenten realisiert habe, möchte ich Ihnen erzĂ€hlen. Was Sie jetzt ĂŒber useEffect lernen, ermöglicht es Ihnen, die offensichtlichen Antworten auf die oben genannten Fragen klar zu sehen.

Aber um die Antworten auf diese Fragen zu sehen, mĂŒssen wir zuerst einen Schritt zurĂŒcktreten. Der Zweck dieses Artikels besteht nicht darin, seinen Lesern schrittweise Anweisungen fĂŒr die Arbeit mit useEffect . Es soll Ihnen useEffect helfen, useEffect " useEffect ". Und ehrlich gesagt gibt es nicht viel zu lernen. TatsĂ€chlich werden wir die meiste Zeit damit verbringen, das zu vergessen, was wir vorher wussten.

Alles in meinem Kopf kam erst zusammen, nachdem ich aufgehört hatte, den useEffect Haken durch das Prisma der bekannten Methoden des Lebenszyklus komponentenbasierter Komponenten zu betrachten.

"Du musst vergessen, was dir beigebracht wurde"


habr.com/ru/company/ruvds/blog/445276/Yoda


Es wird davon ausgegangen, dass der Leser dieses Materials mit der useEffect- API einigermaßen vertraut ist. Dies ist ein ziemlich langer Artikel, der mit einem kleinen Buch verglichen werden kann. Tatsache ist, dass ich es vorziehe, meine Gedanken auf diese Weise auszudrĂŒcken. Im Folgenden werden ganz kurz Antworten auf die oben diskutierten Fragen gegeben. Vielleicht sind sie nĂŒtzlich fĂŒr diejenigen, die nicht die Zeit oder den Wunsch haben, das gesamte Material zu lesen.

Wenn das Format, in dem wir useEffect mit all seinen ErklĂ€rungen und Beispielen betrachten werden, fĂŒr Sie nicht sehr geeignet ist, können Sie etwas warten - bis zu dem Moment, an dem diese ErklĂ€rungen in unzĂ€hligen anderen HandbĂŒchern erscheinen. Hier ist die gleiche Geschichte wie bei der React-Bibliothek selbst, die 2013 etwas völlig Neues war. Es dauert einige Zeit, bis die Entwicklergemeinschaft das neue mentale Modell erkennt und Lehrmaterialien auf der Grundlage dieses Modells erscheinen.

Antworten auf Fragen


Hier finden Sie kurze Antworten auf die am Anfang dieses Materials gestellten Fragen, die fĂŒr diejenigen gedacht sind, die nicht den gesamten Text lesen möchten. Wenn Sie beim Lesen dieser Antworten das GefĂŒhl haben, die Bedeutung des Gelesenen nicht wirklich zu verstehen, lesen Sie das Material durch. Detaillierte ErlĂ€uterungen finden Sie im Text. Wenn Sie alles lesen möchten, können Sie diesen Abschnitt ĂŒberspringen.

▍Wie wird componentDidMount mit useEffect abgespielt?


Obwohl Sie das useEffect(fn, []) verwenden können, um die FunktionalitĂ€t von componentDidMount abzuspielen, entspricht es nicht genau der componentDidMount . Im Gegensatz zu componentDidMount erfasst es nĂ€mlich Eigenschaften und Status. Daher sehen Sie auch innerhalb des RĂŒckrufs die anfĂ€nglichen Eigenschaften und den Status. Wenn Sie die neueste Version von etwas sehen möchten, können Sie es in den ref Link schreiben. Normalerweise gibt es jedoch eine einfachere Möglichkeit, den Code zu strukturieren. Dies ist daher optional. Denken Sie daran, dass sich das Modell fĂŒr mentale Effekte von dem fĂŒr componentDidMount und andere Methoden des Komponentenlebenszyklus geltenden Modell unterscheidet. Daher kann der Versuch, die genauen Äquivalente zu finden, mehr schaden als nĂŒtzen. Um produktiv zu arbeiten, muss man sozusagen "in Effekten denken". Die Grundlage ihres mentalen Modells liegt nĂ€her an der Implementierung der Synchronisation als an der Reaktion auf Ereignisse im Lebenszyklus von Komponenten.

▍Wie werden Daten in useEffect ordnungsgemĂ€ĂŸ geladen? Was []?


Hier finden Sie eine gute Anleitung zum Laden von Daten mit useEffect . Versuchen Sie es vollstĂ€ndig zu lesen! Es ist nicht so groß. Die Klammern [] , die ein leeres Array darstellen, bedeuten, dass der Effekt nicht die Werte verwendet, die am React-Datenstrom beteiligt sind, und aus diesem Grund kann seine einmalige Verwendung als sicher angesehen werden. DarĂŒber hinaus ist die Verwendung eines leeren Arrays von AbhĂ€ngigkeiten eine hĂ€ufige Fehlerquelle fĂŒr den Fall, dass tatsĂ€chlich ein bestimmter Wert fĂŒr den Effekt verwendet wird. Sie mĂŒssen mehrere Strategien beherrschen (hauptsĂ€chlich in Form von useReducer und useCallback ), mit denen Sie die Notwendigkeit einer AbhĂ€ngigkeit beseitigen können, anstatt diese AbhĂ€ngigkeit unangemessen zu verwerfen.

▍ MĂŒssen Funktionen als EffektabhĂ€ngigkeiten angegeben werden?


Es wird empfohlen, Funktionen, fĂŒr die keine Eigenschaften oder ein Status erforderlich sind, außerhalb der Komponenten zu verwenden. Es wird empfohlen, Funktionen, die nur von Effekten verwendet werden, innerhalb der Effekte zu platzieren. Wenn Ihr Effekt danach weiterhin Funktionen verwendet, die sich im Bereich des useCallback befinden (einschließlich Funktionen aus den Eigenschaften), schließen Sie sie in useCallback wo sie deklariert sind, und versuchen Sie, sie erneut zu verwenden. Warum ist das wichtig? Funktionen können Werte aus Eigenschaften und Status „sehen“, sodass sie am Datenstrom teilnehmen. Weitere Informationen hierzu finden Sie in unseren FAQ.

▍ Warum gerĂ€t ein Programm manchmal in eine Endlosschleife zum erneuten Laden von Daten?


Dies kann passieren, wenn das Laden von Daten in einem Effekt ausgefĂŒhrt wird, der kein zweites Argument enthĂ€lt, das die AbhĂ€ngigkeiten darstellt. Ohne diese Option werden nach jedem Rendering-Vorgang Effekte ausgefĂŒhrt. Wenn Sie also den Status festlegen, werden solche Effekte abgerufen. Eine Endlosschleife kann auch auftreten, wenn im AbhĂ€ngigkeitsarray ein Wert angezeigt wird, der sich stĂ€ndig Ă€ndert. Finden Sie heraus, welche Art von Wert möglich ist, indem Sie AbhĂ€ngigkeiten einzeln entfernen. Das Entfernen von AbhĂ€ngigkeiten (oder die vorschnelle Verwendung von [] ) ist jedoch normalerweise der falsche Ansatz zur Lösung eines Problems. Stattdessen sollten Sie die Ursache des Problems finden und es wirklich lösen. Beispielsweise können Funktionen ein Ă€hnliches Problem verursachen. Sie können zur Lösung beitragen, indem Sie sie in Effekte useCallback außerhalb der Komponenten verschieben oder in useCallback . Um das Erstellen mehrerer Objekte zu vermeiden, können Sie useMemo .

▍ Warum ist manchmal der alte Zustand in den Effekten sichtbar oder werden alte Eigenschaften gefunden?


Effekte "sehen" immer Eigenschaften und Status aus dem Rendering, in dem sie deklariert sind. Dies hilft , Fehler zu vermeiden , kann jedoch in einigen FĂ€llen den normalen Betrieb der Komponente beeintrĂ€chtigen. In solchen FĂ€llen können Sie verĂ€nderbare ref explizit verwenden, um mit solchen Werten zu arbeiten (Sie können dies am Ende des oben genannten Artikels lesen). Wenn Sie der Meinung sind, dass Sie Eigenschaften oder den Status des alten Renderings sehen, dies jedoch nicht erwarten, haben Sie möglicherweise einige AbhĂ€ngigkeiten ĂŒbersehen. Verwenden Sie diese Linter-Regel, um zu lernen, sie zu sehen. In ein paar Tagen wird es so etwas wie Ihre zweite Natur. Schauen Sie sich auch diese Antwort in unseren FAQ an.

Ich hoffe, diese Antworten auf die Fragen waren fĂŒr diejenigen, die sie lesen, nĂŒtzlich. Lassen Sie uns nun mehr ĂŒber useEffect sprechen.

Jeder Render hat seine eigenen Eigenschaften und seinen eigenen Status.


Bevor wir die Effekte diskutieren können, mĂŒssen wir ĂŒber das Rendern sprechen.

Hier ist die funktionale ZĂ€hlerkomponente.

 function Counter() { const [count, setCount] = useState(0); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

Schauen <p>You clicked {count} times</p> . Was meint sie Beobachtet die konstante Konstante irgendwie Änderungen im Zustand und wird sie automatisch aktualisiert? Diese Schlussfolgerung kann als eine Art wertvolle erste Idee von jemandem angesehen werden, der React studiert, aber es ist kein genaues mentales Modell dessen, was passiert.

In unserem Beispiel ist count nur eine Zahl. Dies ist keine Art von magischer „Datenbindung“, keine Art von „Beobachterobjekt“ oder „Proxy“ oder irgendetwas anderes. Vor uns liegt eine alte gute Nummer wie diese:

 const count = 42; // ... <p>You clicked {count} times</p> // ... 

WĂ€hrend der ersten Komponentenausgabe ist der von useState() erhaltene useState() 0. Wenn wir setCount(1) aufrufen, ruft React die Komponente erneut auf. Diese count ist 1. Und so weiter:

 //     function Counter() { const count = 0; //  useState() // ... <p>You clicked {count} times</p> // ... } //       function Counter() { const count = 1; //  useState() // ... <p>You clicked {count} times</p> // ... } //        function Counter() { const count = 2; //  useState() // ... <p>You clicked {count} times</p> // ... } 

React ruft die Komponente auf, wenn wir den Status aktualisieren. Infolgedessen "sieht" jede Rendering-Operation ihren eigenen Wert des counter , der innerhalb der Funktion eine Konstante ist.

Infolgedessen fĂŒhrt diese Zeile keine spezielle Datenbindungsoperation aus:

 <p>You clicked {count} times</p> 

Es wird nur ein numerischer Wert in den Code eingebettet, der beim Rendern generiert wird. Diese Nummer wird von React bereitgestellt. Wenn wir setCount , ruft React die Komponente erneut mit einem anderen count . React aktualisiert dann das DOM, sodass das Dokumentobjektmodell mit der neuesten Datenausgabe beim Rendern von Komponenten ĂŒbereinstimmt.

Die wichtigste Schlussfolgerung, die daraus gezogen werden kann, ist, dass die count eine Konstante innerhalb eines bestimmten Renderings ist und sich im Laufe der Zeit nicht Ă€ndert. Die Komponente, die immer wieder aufgerufen wird, Ă€ndert sich. Jedes Rendern "sieht" seinen eigenen count , der fĂŒr jede der RendervorgĂ€nge isoliert ist.

In diesem Material finden Sie Details zu diesem Prozess.

Jeder Render hat seine eigenen Ereignishandler.


Alles ist noch klar. Was ist mit Event-Handlern?
Schauen Sie sich dieses Beispiel an. Hier wird drei Sekunden nach dem Klicken auf die SchaltflÀche ein Meldungsfeld mit Informationen zu dem in count gespeicherten Wert angezeigt:

 function Counter() { const [count, setCount] = useState(0); function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>     <button onClick={handleAlertClick}>       Show alert     </button>   </div> ); } 

Angenommen, ich fĂŒhre die folgende Abfolge von Aktionen aus:

  • Ich werde den Wert von count auf 3 bringen, indem Click me SchaltflĂ€che " Click me klicke.
  • Klicken Sie auf die SchaltflĂ€che Show alert anzeigen.
  • Erhöhen Sie den Wert vor Ablauf des Zeitlimits auf 5.


Erhöhen des ZÀhlwerts nach Klicken auf die SchaltflÀche Benachrichtigung anzeigen

Was erscheint Ihrer Meinung nach im Meldungsfeld? Wird dort 5 angezeigt, was dem count zum Zeitpunkt des Auslösens des Timers entspricht, oder 3 - also der count zum Zeitpunkt des Tastendrucks?

Jetzt finden Sie die Antwort auf diese Frage. Wenn Sie jedoch alles selbst herausfinden möchten, finden Sie hier eine funktionierende Version dieses Beispiels.

Wenn Ihnen das, was Sie gesehen haben, unverstÀndlich erscheint, finden Sie hier ein Beispiel, das der RealitÀt nÀher kommt. Stellen Sie sich eine Chat-Anwendung vor, in der im Status die ID aktuellen EmpfÀngers der Nachricht gespeichert ist und eine SchaltflÀche zum Send vorhanden ist. In diesem Material wird das Geschehen im Detail betrachtet. TatsÀchlich lautet die richtige Antwort auf die Frage, was im Meldungsfeld angezeigt wird, 3.

Der Mechanismus zum Anzeigen eines Meldungsfelds hat den Status zum Zeitpunkt des Klickens auf die SchaltflĂ€che „erfasst“.

Es gibt Möglichkeiten, eine andere Version des Verhaltens zu implementieren, aber im Moment werden wir uns mit dem Standardverhalten des Systems befassen. Beim Aufbau mentaler Technologiemodelle ist es wichtig, den "Weg des geringsten Widerstands" von allen Arten von "NotausgÀngen" zu unterscheiden.

Wie funktioniert das alles?

Wir haben bereits gesagt, dass der Wert von count eine Konstante fĂŒr jeden spezifischen Aufruf unserer Funktion ist. Ich denke, es lohnt sich, nĂ€her darauf einzugehen. Der Punkt ist, dass unsere Funktion viele Male aufgerufen wird (einmal fĂŒr jede Renderoperation), aber bei jedem dieser Aufrufe ist die count darin eine Konstante. Diese Konstante wird auf einen bestimmten Wert gesetzt (der den Status einer bestimmten Renderoperation darstellt).

Dieses Verhalten von Funktionen ist nichts Besonderes fĂŒr React - gewöhnliche Funktionen verhalten sich Ă€hnlich:

 function sayHi(person) { const name = person.name; setTimeout(() => {   alert('Hello, ' + name); }, 3000); } let someone = {name: 'Dan'}; sayHi(someone); someone = {name: 'Yuzhi'}; sayHi(someone); someone = {name: 'Dominic'}; sayHi(someone); 

In diesem Beispiel wird die externe Variable someone mehrmals neu zugewiesen. Dasselbe kann irgendwo in React passieren, der aktuelle Status der Komponente kann sich Ă€ndern. Innerhalb der sayHi Funktion gibt es jedoch einen lokalen Konstantennamen, der der person eines bestimmten Anrufs zugeordnet ist. Diese Konstante ist lokal, daher sind ihre Werte in verschiedenen Funktionsaufrufen voneinander isoliert! Infolgedessen „merkt“ sich jedes angezeigte Nachrichtenfenster nach einer ZeitĂŒberschreitung seinen eigenen name .

Dies erklĂ€rt, wie unser Ereignishandler den count erfasst, wenn auf eine SchaltflĂ€che geklickt wird. Wenn wir bei der Arbeit mit Komponenten dasselbe Prinzip anwenden, stellt sich heraus, dass jedes Rendering seinen eigenen count „sieht“:

 //     function Counter() { const count = 0; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } //       function Counter() { const count = 1; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } //        function Counter() { const count = 2; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } 

Infolgedessen gibt jedes Rendering tatsÀchlich seine eigene "Version" handleAlertClick . Jede dieser Versionen "merkt" sich ihren eigenen count :

 //     function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 0);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   0 // ... } //       function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 1);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   1 // ... } //        function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 2);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   2 // ... } 

Aus diesem Grund gehören in diesem Beispiel Ereignishandler zu bestimmten Renderings. Wenn Sie auf die SchaltflÀche klicken, verwendet die Komponente den count dieser Renderings.

Innerhalb eines bestimmten Renderings bleiben die Eigenschaften und der Status immer gleich. Wenn jedoch verschiedene Rendering-VorgĂ€nge ihre eigenen Eigenschaften und ihren eigenen Status verwenden, geschieht dasselbe mit allen Mechanismen, die sie verwenden (einschließlich Ereignishandlern). Sie "gehören" auch zu bestimmten Renderings. Daher sehen selbst asynchrone Funktionen in Ereignishandlern dieselben count .

Es ist zu beachten, dass ich im obigen Beispiel die spezifischen count direkt in die handleAlertClick Funktion eingebettet handleAlertClick . Dieser "mentale" Ersatz wird uns nicht schaden, da die konstante count innerhalb eines bestimmten Renderings nicht geĂ€ndert werden kann. Erstens ist es eine Konstante und zweitens ist es eine Zahl. Man kann mit Sicherheit sagen, dass man auch ĂŒber andere Bedeutungen wie Objekte nachdenken kann, aber nur, wenn wir in der Regel akzeptieren, keine Änderungen (Mutationen) im Zustand vorzunehmen. Gleichzeitig sind wir mit dem Aufruf von setSomething(newObj) mit einem neuen Objekt zufrieden, anstatt das vorhandene zu Ă€ndern, da bei diesem Ansatz der Status des vorherigen Renderings unberĂŒhrt bleibt.

Jeder Render hat seine eigenen Effekte.


Wie Sie wissen, ist dieses Material den Effekten gewidmet, aber wir haben noch nicht einmal darĂŒber gesprochen. Jetzt werden wir es beheben. Wie sich herausstellt, unterscheidet sich die Arbeit mit Effekten nicht besonders von dem, was wir bereits herausgefunden haben.

Betrachten Sie ein Beispiel aus der Dokumentation, das dem bereits analysierten sehr Àhnlich ist:

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   document.title = `You clicked ${count} times`; }); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

Jetzt habe ich eine Frage an Sie. Wie liest ein Effekt den letzten count ?

Vielleicht wird hier eine "Datenbindung" verwendet oder ein "Beobachterobjekt", das den count innerhalb der Effektfunktion aktualisiert? Vielleicht ist count eine verÀnderbare Variable, deren Wert React in unserer Komponente festlegt, wodurch der Effekt immer die neueste Version sieht?

Nein.

Wir wissen bereits, dass beim Rendern einer bestimmten Komponente die count eine Konstante ist. Sogar Ereignishandler "sehen" den count aus dem Rendering, zu dem sie "gehören", da count eine Konstante ist, die sich in einem bestimmten Bereich befindet. Gleiches gilt fĂŒr Effekte!

Und es sollte beachtet werden, dass dies nicht die count der Variablen ist count die sich innerhalb des "unverÀnderten" Effekts irgendwie Àndert. Vor uns liegt die Funktion des Effekts selbst, die bei jedem Rendering-Vorgang unterschiedlich ist.

Jede Version "sieht" den count aus dem Render, zu dem sie "gehört":

 //     function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${0} times`;   } ); // ... } //       function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${1} times`;   } ); // ... } //        function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${2} times`;   } ); // .. } 

React , DOM .

, ( ), , , , «» , «».

, , .

, ( , ). , , , .

, , :

React:

  • , 0.

:

  • : <p>You clicked 0 times</p> .
  • , , : () => { document.title = 'You clicked 0 times' } .

React:

  • NatĂŒrlich. . , , - DOM.

:

  • , .

React:

  • , , .
  • () => { document.title = 'You clicked 0 times' } .

, . , , - :

:

  • , React, 1.

React:

  • , 1.

:

  • : <p>You clicked 1 times</p> .
  • , , : () => { document.title = 'You clicked 1 times' } .

React:

  • NatĂŒrlich. . , , - DOM.

:

  • , .

React:

  • , , .
  • () => { document.title = 'You clicked 1 times' } .





, , , , «» .

. :

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   setTimeout(() => {     console.log(`You clicked ${count} times`);   }, 3000); }); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

, ?

, . , , , . ! , , , , , count . .




: «, ! ?».

, , this.setState , , . , , , , , :

   componentDidUpdate() {   setTimeout(() => {     console.log(`You clicked ${this.state.count} times`);   }, 3000); } 

, this.state.count count , , . , , , 5 , 5 .




, JavaScript-, , , , , setTimeout , . , (React this.state , ), .

— , , «» , . , , , . , , . , , , , , , .


, ( , , - API ) , .

:

 function Example(props) { useEffect(() => {   setTimeout(() => {     console.log(props.counter);   }, 1000); }); // ... } function Example(props) { const counter = props.counter; useEffect(() => {   setTimeout(() => {     console.log(counter);   }, 1000); }); // ... } 

, «» . ! . , .

, , - , , , , . , ref , .

, , , , , . , ( ), «» React-. , , . , .

, , , :

 function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => {   //        count   latestCount.current = count;   setTimeout(() => {     //            console.log(`You clicked ${latestCount.current} times`);   }, 3000); }); // ... 




- React . React this.state . , , latestCount.current . , . , , , .

?


, . , , «» .

:

 useEffect(() => {   ChatAPI.subscribeToFriendStatus(props.id, handleStatusChange);   return () => {     ChatAPI.unsubscribeFromFriendStatus(props.id, handleStatusChange);   }; }); 

, props — {id: 10} , {id: 20} — . , :

  • React {id: 10} .
  • React {id: 20} .
  • React {id: 20} .

( , , .)

, «» - , , «» - , . — , , , . .

React , . , . . . :

  • React {id: 20} .
  • . {id: 20} .
  • React {id: 10} .
  • React {id: 20} .

, «» props , {id: 10} , , props {id: 20} .

, 



— ?

: « ( , , - API ) , ».

! « » , . , , :

 //  ,  props  {id: 10} function Example() { // ... useEffect(   //       () => {     ChatAPI.subscribeToFriendStatus(10, handleStatusChange);     //           return () => {       ChatAPI.unsubscribeFromFriendStatus(10, handleStatusChange);     };   } ); // ... } //  ,  props  {id: 20} function Example() { // ... useEffect(   //       () => {     ChatAPI.subscribeToFriendStatus(20, handleStatusChange);     //           return () => {       ChatAPI.unsubscribeFromFriendStatus(20, handleStatusChange);     };   } ); // ... } 

, , 
 , «» , -, {id: 10} .

React . , , . props , .

,


React , . .

, :

 function Greeting({ name }) { return (   <h1 className="Greeting">     Hello, {name}   </h1> ); } 

, <Greeting name="Dan" /> , — <Greeting name="Yuzhi" /> , <Greeting name="Yuzhi" /> . Hello, Yuzhi .

, , . React, . , , . $.addClass $.removeClass jQuery- ( — , «»), , CSS- React ( — , «»).

React DOM , . «» «».

. useEffect , React, .

 function Greeting({ name }) { useEffect(() => {   document.title = 'Hello, ' + name; }); return (   <h1 className="Greeting">     Hello, {name}   </h1> ); } 

useEffect , , . , - , ! , «», «».

, A , B , — C , , C . (, - ), .

, , , . ( ).

Wie gehe ich damit um?

React


React DOM. DOM , React DOM, - .

, :

 <h1 className="Greeting"> Hello, Dan </h1> 

:

 <h1 className="Greeting"> Hello, Yuzhi </h1> 

React :

 const oldProps = {className: 'Greeting', children: 'Hello, Dan'}; const newProps = {className: 'Greeting', children: 'Hello, Yuzhi'}; 

React , children , DOM. , className . :

 domNode.innerText = 'Hello, Yuzhi'; // domNode.className    

- ? , , .

, , - :

 function Greeting({ name }) { const [counter, setCounter] = useState(0); useEffect(() => {   document.title = 'Hello, ' + name; }); return (   <h1 className="Greeting">     Hello, {name}     <button onClick={() => setCounter(counter + 1)}>       Increment     </button>   </h1> ); } 

counter . document.title name , name . document.title counter , .

React 
 ?

 let oldEffect = () => { document.title = 'Hello, Dan'; }; let newEffect = () => { document.title = 'Hello, Dan'; }; //   React  ,        ? 

— . React , , . ( . name .)

, , ( deps ), useEffect :

   useEffect(() => {   document.title = 'Hello, ' + name; }, [name]); //   

, React: «, , , , name ».

, , React :

 const oldEffect = () => { document.title = 'Hello, Dan'; }; const oldDeps = ['Dan']; const newEffect = () => { document.title = 'Hello, Dan'; }; const newDeps = ['Dan']; // React     ,     . //      ,     . 

, , ! - - .

React


React — . , , , , useEffect , , , . ( !)

 function SearchResults() { async function fetchData() {   // ... } useEffect(() => {   fetchData(); }, []); //   ?  .      . // ... } 

FAQ , . .

« !», — . : , , . , , , — , .

, , . , , , , . , . .

, , .

, React


, , React , .

   useEffect(() => {   document.title = 'Hello, ' + name; }, [name]); 


—

, , , [] , , , , :

   useEffect(() => {   document.title = 'Hello, ' + name; }, []); // :    name 


—

. , «» , , .

, , , . , : « setInterval clearInterval ». . , , , useEffect , , , [] . - , ?

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, []); return <h1>{count}</h1>; } 

, , .

, « , », . , , , setInterval , . , ?

, — React , , . , count , React , , , . — .

count 0. setCount(count + 1) setCount(0 + 1) . , — [] , setCount(0 + 1) :

 //  ,   0 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(0 + 1); //  setCount(1)     }, 1000);     return () => clearInterval(id);   },   [] //    ); // ... } //       1 function Counter() { // ... useEffect(   //     - ,    //   React  ,   .   () => {     const id = setInterval(() => {       setCount(1 + 1);     }, 1000);     return () => clearInterval(id);   },   [] ); // ... } 

React, , , — .

count — , ( ):

   const count = // ... useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, []); 

. React .


,

. , , React , , . — - .


React , . , , , .

, , , . count :

 useEffect(() => { const id = setInterval(() => {   setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); 

. , , — , . count , count , setCount(count + 1) :

 //  ,   0 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(0 + 1); // setCount(count + 1)     }, 1000);     return () => clearInterval(id);   },   [0] // [count] ); // ... } //  ,   1 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(1 + 1); // setCount(count + 1)     }, 1000);     return () => clearInterval(id);   },   [1] // [count] ); // ... } 

, setInterval , count , . , .


,

, , , . — , .

.


, count .

 useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, [count]); 

, , count . , count setCount . , , count . , , setState :

   useEffect(() => {   const id = setInterval(() => {     setCount(c => c + 1);   }, 1000);   return () => clearInterval(id); }, []); 

« ». , count - , setCount(count + 1) . count - , count + 1 «» React. React count . , React — , , , .

setCount(c => c + 1) . « React », , . « » , , .

, , , . React. count :


,

.

, setInterval , , c => c + 1 . count . React .

Google Docs


, , — ? , , «», , . , Google Docs . . , .

, . . , setCount(c => c + 1) , , setCount(count + 1) , «» count . , ( — «»). « React» — . .

( ) , Google Docs . — , React . , , ( , , ) .

, setCount(c => c + 1) , . , . , , , , , . setCount(c => c + 1) . useReducer .


, : count step . setInterval , step :

 function Counter() { const [count, setCount] = useState(0); const [step, setStep] = useState(1); useEffect(() => {   const id = setInterval(() => {     setCount(c => c + step);   }, 1000);   return () => clearInterval(id); }, [step]); return (   <>     <h1>{count}</h1>     <input value={step} onChange={e => setStep(Number(e.target.value))} />   </> ); } 

.

, React . step , . .

: step setInterval — step . , , , ! , , , , , .

, , , setInterval , step . step ?

, , useReducer .

, setSomething(something => ...) , , . «», , , .

step dispatch :

 const [state, dispatch] = useReducer(reducer, initialState); const { count, step } = state; useEffect(() => { const id = setInterval(() => {   dispatch({ type: 'tick' }); //  setCount(c => c + step); }, 1000); return () => clearInterval(id); }, [dispatch]); 

.

: « , ?». , React , dispatch . .

!

( dispatch setstate useRef , React , . — .)

, , , , . step . , . , . :

 const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') {   return { count: count + step, step }; } else if (action.type === 'step') {   return { count, step: action.step }; } else {   throw new Error(); } } 

, , , .

useReducer — -


, , , . , , ? , , API <Counter step={1} /> . , props.step ?

, ! , :

 function Counter({ step }) { const [count, dispatch] = useReducer(reducer, 0); function reducer(state, action) {   if (action.type === 'tick') {     return state + step;   } else {     throw new Error();   } } useEffect(() => {   const id = setInterval(() => {     dispatch({ type: 'tick' });   }, 1000);   return () => clearInterval(id); }, [dispatch]); return <h1>{count}</h1>; } 

, . , , , , . .

dispatch . , , . .

, , . «» , , ? , dispatch , React . . .

useReducer «-» . , . , , , , .


, - , .

, , , :

 function SearchResults() { const [data, setData] = useState({ hits: [] }); async function fetchData() {   const result = await axios(     'https://hn.algolia.com/api/v1/search?query=react',   );   setData(result.data); } useEffect(() => {   fetchData(); }, []); //   ? // ... 

, .

, , . , , , , , , , , .

, , , , :

 function SearchResults() { // ,       function getFetchUrl() {   return 'https://hn.algolia.com/api/v1/search?query=react'; } // ,        async function fetchData() {   const result = await axios(getFetchUrl());   setData(result.data); } useEffect(() => {   fetchData(); }, []); // ... } 

, , :

 function SearchResults() { const [query, setQuery] = useState('react'); // ,       function getFetchUrl() {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } // ,        async function fetchData() {   const result = await axios(getFetchUrl());   setData(result.data); } useEffect(() => {   fetchData(); }, []); // ... } 

, (, ), . .

, . , :

 function SearchResults() { // ... useEffect(() => {   //      !   function getFetchUrl() {     return 'https://hn.algolia.com/api/v1/search?query=react';   }   async function fetchData() {     const result = await axios(getFetchUrl());     setData(result.data);   }   fetchData(); }, []); //    . // ... } 

.

? , « ». React, - .

getFetchUrl , query , , , , . — , query :

 function SearchResults() { const [query, setQuery] = useState('react'); useEffect(() => {   function getFetchUrl() {     return 'https://hn.algolia.com/api/v1/search?query=' + query;   }   async function fetchData() {     const result = await axios(getFetchUrl());     setData(result.data);   }   fetchData(); }, [query]); //    . // ... } 

.

, « React». query . , , , , . , , .

exhaustive-deps eslint-plugin-react-hooks , . , , .




Es ist sehr bequem.

, ?


. , , . , , .

? , . : React . . , « ». , , . , , , !

, , . , getFetchUrl :

 function SearchResults() { function getFetchUrl(query) {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, []); //  : getFetchUrl useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, []); //  : getFetchUrl // ... } 

getFetchUrl — , .

, «» , . getFetchUrl (, , ), :

 function SearchResults() { //       function getFetchUrl(query) {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => {   const url = getFetchUrl('react');   // ...    -    ... }, [getFetchUrl]); //   ,     . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, [getFetchUrl]); //   ,     . // ... } 

, getFetchUrl . , — . - , , . , , , .

— .

, , :

 //        function getFetchUrl(query) { return 'https://hn.algolia.com/api/v1/search?query=' + query; } function SearchResults() { useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, []); //     . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, []); //     . // ... } 

, . , , .

. , useCallback :

 function SearchResults() { //    ,   const getFetchUrl = useCallback((query) => {   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []);  //      . useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, [getFetchUrl]); //      . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, [getFetchUrl]); //       // ... } 

useCallback . : , -, , , .

, . ( 'react' 'redux' ). , , , query . , , query , getFetchUrl .

, query useCallback :

 function SearchResults() { const [query, setQuery] = useState('react'); const getFetchUrl = useCallback(() => { //   query   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []); //  : query // ... } 

useCallback query , , getFetchUrl , query :

 function SearchResults() { const [query, setQuery] = useState('react'); //      query const getFetchUrl = useCallback(() => {   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, [query]);  //    . useEffect(() => {   const url = getFetchUrl();   // ...    -   ... }, [getFetchUrl]); //    . // ... } 

useCallback , query , getFetchUrl , , . query , getFetchUrl , . Excel: - , , , .

— , . , :

 function Parent() { const [query, setQuery] = useState('react'); //      query const fetchData = useCallback(() => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + query;   // ...      ... }, [query]);  //       return <Child fetchData={fetchData} /> } function Child({ fetchData }) { let [data, setData] = useState(null); useEffect(() => {   fetchData().then(setData); }, [fetchData]); //       // ... } 

fetchData Parent query , Child , .

?


, , , , . , , , , :

 class Parent extends Component { state = {   query: 'react' }; fetchData = () => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;   // ...    -   ... }; render() {   return <Child fetchData={this.fetchData} />; } } class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } render() {   // ... } } 

, : « , , , useEffect — componentDidMount componentDidUpdate . !». componentDidUpdate :

 class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } componentDidUpdate(prevProps) {   //         if (this.props.fetchData !== prevProps.fetchData) {     this.props.fetchData();   } } render() {   // ... } } 

, fetchData — ! (, , , .) - , . this.props.fetchData prevProps.fetchData . , , ?

   componentDidUpdate(prevProps) {   this.props.fetchData(); } 

. . ( .) , fetchData this.state.query ?

   render() {   return <Child fetchData={this.fetchData.bind(this, this.state.query)} />; } 

this.props.fetchData !== prevProps.fetchData true , , query ! .

, , , query Child . , , query , query :

 class Parent extends Component { state = {   query: 'react' }; fetchData = () => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;   // ...    -    ... }; render() {   return <Child fetchData={this.fetchData} query={this.state.query} />; } } class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } componentDidUpdate(prevProps) {   if (this.props.query !== prevProps.query) {     this.props.fetchData();   } } render() {   // ... } } 

, , - , , .

, , . this , . , , , , - . , this.props.fetchData , , , , , .

- useCallback . , , , . , . useCallback props.fetchData .

, useMemo :

 function ColorPicker() { //         Child, //       . const [color, setColor] = useState('pink'); const style = useMemo(() => ({ color }), [color]); return <Child style={style} />; } 

, useCallback , - . « », , , . , . , .

, fetchData ( ), . , , . (« props.onComplete , ?») , .


, :

 class Article extends Component { state = {   article: null }; componentDidMount() {   this.fetchData(this.props.id); } async fetchData(id) {   const article = await API.fetchArticle(id);   this.setState({ article }); } // ... } 

, , , . . — , :

 class Article extends Component { state = {   article: null }; componentDidMount() {   this.fetchData(this.props.id); } componentDidUpdate(prevProps) {   if (prevProps.id !== this.props.id) {     this.fetchData(this.props.id);   } } async fetchData(id) {   const article = await API.fetchArticle(id);   this.setState({ article }); } // ... } 

, , , . , . , {id: 10} , {id: 20} , , . , , , . .

, , . — , , async/await ( , - ) , ( , ).

, async -. (, , , , .)

, , ! .

, :

 function Article({ id }) { const [article, setArticle] = useState(null); useEffect(() => {   let didCancel = false;   async function fetchData() {     const article = await API.fetchArticle(id);     if (!didCancel) {       setArticle(article);     }   }   fetchData();   return () => {     didCancel = true;   }; }, [id]); // ... } 

, , , . , .


, , , , , . , , , . . — .

useEffect , , , . React. , useEffect .

, , « », . . , , , , «» , .

, useEffect , . — . — , , — , . , , , , API.

, , useFetch , , useTheme , . , , useEffect . , , , .

, , useEffect . — , . , . ?

Suspense React , , - ( : , , ) .

Suspense , , useEffect , , , - . , , , . , , , , .

Zusammenfassung


, . , , - , , , .

Source: https://habr.com/ru/post/de445276/


All Articles