Guide d'utilisation complet

Vous avez écrit plusieurs composants à l'aide de crochets . Peut-être - ils ont même créé une petite application. En général, vous êtes plutôt satisfait du résultat. Vous êtes habitué à l'API et, au cours du processus, vous avez trouvé des astuces utiles non évidentes. Vous avez même créé certains de vos propres crochets et réduit votre code à 300 lignes, en y plaçant ce qui était précédemment représenté par la répétition de fragments de programme. Ce que vous avez fait, vous l'avez montré à mes collègues. "Bien joué", ont-ils dit à propos de votre projet.


Mais parfois, lorsque vous utilisez useEffect , les composants des mécanismes logiciels ne s'assemblent pas très bien. Il vous semble que vous manquez quelque chose. Tout cela est similaire à l'utilisation d'événements de cycle de vie de composants basés sur une classe ... mais est-ce vraiment le cas?
En essayant de comprendre ce qui ne vous convient pas exactement, vous remarquez que vous posez les questions suivantes:

  • Comment jouer à componentDidMount utilisant useEffect ?
  • Comment charger des données dans useEffect ? Qu'est-ce que [] ?
  • Les fonctions doivent-elles être spécifiées en tant que dépendances d'effet?
  • Pourquoi un programme se retrouve-t-il parfois dans une boucle sans fin de données de rechargement?
  • Pourquoi l'ancien état est-il parfois visible à l'intérieur des effets ou de vieilles propriétés sont-elles trouvées?

Lorsque j'ai commencé à utiliser des crochets, ces questions m'ont également tourmenté. Même lorsque je préparais la documentation, je ne pouvais pas dire que je maîtrisais parfaitement certaines subtilités. Depuis, j'ai eu plusieurs moments où, soudain, réalisant quelque chose d'important, j'ai vraiment voulu m'exclamer: "Eurêka!" À propos de ce que j'ai réalisé à ces moments, je veux vous le dire. Ce que vous apprenez sur useEffect maintenant vous permettra de voir clairement les réponses évidentes aux questions ci-dessus.

Mais pour voir les réponses à ces questions, nous devons d'abord prendre du recul. Le but de cet article n'est pas de donner à ses lecteurs des instructions pas à pas pour travailler avec useEffect . Il vise à vous aider, pour ainsi useEffect " useEffect " useEffect . Et franchement, il n'y a pas grand-chose à apprendre. En fait, la plupart du temps, nous passerons à oublier ce que nous savions auparavant.

Tout dans ma tête ne s'est réuni qu'après avoir arrêté de regarder le crochet useEffect travers le prisme des méthodes familières du cycle de vie des composants à base de composants.

"Vous devez oublier ce que vous avez appris"


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


Il est supposé que le lecteur de ce document connaît quelque peu l'API useEffect . Il s'agit d'un article assez long, il peut être comparé à un petit livre. Le fait est que je préfère exprimer mes pensées de cette façon. Ci-dessous, très brièvement, des réponses sont données aux questions qui ont été discutées ci-dessus. Peut-être sont-ils utiles à ceux qui n'ont pas le temps ou le désir de lire tout le matériel.

Si le format dans lequel nous allons considérer useEffect , avec toutes ses explications et exemples, ne vous convient pas, vous pouvez attendre un peu - jusqu'au moment où ces explications apparaissent dans d'innombrables autres manuels. Voici la même histoire qu'avec la bibliothèque React elle-même, qui en 2013 était quelque chose de complètement nouveau. Il faut un certain temps à la communauté du développement pour reconnaître le nouveau modèle mental et pour que le matériel pédagogique basé sur ce modèle apparaisse.

Réponses aux questions


Voici de brèves réponses aux questions posées au début de ce document, destinées à ceux qui ne souhaitent pas lire l'intégralité de ce texte. Si, en lisant ces réponses, vous sentez que vous ne comprenez pas vraiment le sens de ce que vous lisez, parcourez le matériel. Vous trouverez des explications détaillées dans le texte. Si vous allez tout lire, vous pouvez ignorer cette section.

▍Comment lire le composantDidMount en utilisant useEffect?


Bien que vous puissiez utiliser la construction useEffect(fn, []) pour lire la fonctionnalité componentDidMount , ce n'est pas l'équivalent exact de componentDidMount . À savoir, contrairement à componentDidMount , il capture les propriétés et l'état. Par conséquent, même à l'intérieur du rappel, vous verrez les propriétés et l'état initiaux. Si vous voulez voir la dernière version de quelque chose, vous pouvez l'écrire dans le lien ref . Mais généralement, il existe un moyen plus simple de structurer le code, ce qui est facultatif. N'oubliez pas que le modèle des effets mentaux est différent de celui applicable à componentDidMount et aux autres méthodes de cycle de vie des composants. Par conséquent, essayer de trouver les équivalents exacts peut faire plus de mal que de bien. Pour travailler de manière productive, vous avez besoin, pour ainsi dire, de «penser en effets». La base de leur modèle mental est plus proche de la mise en œuvre de la synchronisation que de la réponse aux événements du cycle de vie des composants.

OwComment charger correctement les données dans useEffect? Qu'est-ce que []?


Voici un bon guide sur le chargement des données à l'aide de useEffect . Essayez de le lire dans son intégralité! Ce n'est pas aussi gros que ça. Les crochets, [] , représentant un tableau vide, signifient que l'effet n'utilise pas les valeurs participant au flux de données React, et pour cette raison son utilisation unique peut être considérée comme sûre. De plus, l'utilisation d'un tableau vide de dépendances est une source d'erreur courante dans le cas où une certaine valeur est effectivement utilisée dans l'effet. Vous devrez maîtriser plusieurs stratégies (principalement présentées sous la forme useReducer et useCallback ) qui peuvent aider à éliminer le besoin d'une dépendance plutôt que de rejeter cette dépendance de manière déraisonnable.

▍ Les fonctions doivent-elles être spécifiées en tant que dépendances d'effet?


Il est recommandé que les fonctions qui n'ont pas besoin de propriétés ou d'un état soient prises en dehors des composants, et les fonctions qui sont utilisées uniquement par les effets sont recommandées pour être placées à l'intérieur des effets. Si après cela, votre effet utilise toujours des fonctions qui se trouvent dans la portée du rendu (y compris les fonctions des propriétés), enveloppez-les dans useCallback où elles sont déclarées et essayez de les réutiliser. Pourquoi est-ce important? Les fonctions peuvent «voir» les valeurs des propriétés et de l'état, elles participent donc au flux de données. Voici des informations plus détaillées à ce sujet dans notre FAQ.

▍ Pourquoi un programme se retrouve-t-il parfois dans une boucle sans fin de données de rechargement?


Cela peut se produire lorsque le chargement des données est effectué dans un effet qui n'a pas de second argument représentant les dépendances. Sans cela, les effets sont effectués après chaque opération de rendu - ce qui signifie que le réglage de l'état entraînera le rappel de ces effets. Une boucle infinie peut également se produire si une valeur qui change toujours est indiquée dans le tableau de dépendances. Découvrez le type de valeur possible en supprimant les dépendances une par une. Cependant, la suppression des dépendances (ou l'utilisation imprudente de [] ) n'est généralement pas la bonne approche pour résoudre un problème. Au lieu de cela, vous devriez trouver la source du problème et vraiment le résoudre. Par exemple, les fonctions peuvent provoquer un problème similaire. Vous pouvez aider à le résoudre en les mettant dans des effets, en les déplaçant hors des composants ou en les useCallback dans useCallback . Pour éviter de créer plusieurs objets, vous pouvez utiliser useMemo .

▍ Pourquoi parfois l'ancien état est visible à l'intérieur des effets ou de vieilles propriétés sont-elles trouvées?


Les effets «voient» toujours les propriétés et l'état à partir du rendu dans lequel ils sont déclarés. Cela permet d' éviter les erreurs , mais dans certains cas, cela peut interférer avec le fonctionnement normal du composant. Dans de tels cas, vous pouvez utiliser explicitement des liens ref mutables pour travailler avec de telles valeurs (vous pouvez en lire plus à la fin de l'article susmentionné). Si vous pensez que vous voyez des propriétés ou un état de l'ancien rendu, mais ne vous y attendez pas, vous avez peut-être manqué certaines dépendances. Pour apprendre à les voir, utilisez cette règle du linter. Dans quelques jours, cela deviendra quelque chose comme votre seconde nature. Jetez également un œil à cette réponse dans notre FAQ.

J'espère que ces réponses aux questions ont été utiles à ceux qui les ont lues. Parlons maintenant davantage de useEffect .

Chaque rendu a ses propres propriétés et états.


Avant de pouvoir discuter des effets, nous devons parler de rendu.

Voici le compteur fonctionnel.

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

Examinez de près la ligne <p>You clicked {count} times</p> . Que veut-elle dire? La constante constante «observe»-t-elle en quelque sorte les changements d'état et est-elle mise à jour automatiquement? Cette conclusion peut être considérée comme une sorte de première idée valable de quelqu'un qui étudie React, mais ce n'est pas un modèle mental précis de ce qui se passe.

Dans notre exemple, le count n'est qu'un nombre. Ce n'est pas une sorte de «liaison de données» magique, pas une sorte d '«objet observateur» ou de «proxy», ou quoi que ce soit d'autre. Nous avons devant nous un bon vieux numéro, comme celui-ci:

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

Lors de la première sortie du composant, la valeur de count obtenue à partir de useState() est 0. Lorsque nous appelons setCount(1) , React appelle à nouveau le composant. Ce count temps sera 1. Et ainsi de suite:

 //     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 appelle le composant chaque fois que nous mettons à jour l'état. Par conséquent, chaque opération de rendu «voit» sa propre valeur de l'état de counter , qui, à l'intérieur de la fonction, est une constante.

Par conséquent, cette ligne n'effectue aucune opération de liaison de données spéciale:

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

Il n'incorpore qu'une valeur numérique dans le code généré lors du rendu. Ce numéro est fourni par React. Lorsque nous appelons setCount , React appelle à nouveau le composant avec une valeur de count différente. React met ensuite à jour le DOM afin que le modèle d'objet de document corresponde à la dernière sortie de données lors du rendu du composant.

La conclusion la plus importante que l'on peut en tirer est que le count est une constante à l'intérieur d'un rendu particulier et ne change pas au fil du temps. Le composant qui est appelé encore et encore change. Chaque rendu "voit" sa propre valeur de count , qui est isolée pour chacune des opérations de rendu.

Dans ce document, vous pouvez trouver des détails sur ce processus.

Chaque rendu a ses propres gestionnaires d'événements.


Tout est encore clair. Qu'en est-il des gestionnaires d'événements?
Jetez un oeil à cet exemple. Ici, trois secondes après avoir cliqué sur le bouton, une boîte de message s'affiche avec des informations sur la valeur stockée dans count :

 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> ); } 

Supposons que j'exécute la séquence d'actions suivante:

  • Je vais porter la valeur de count à 3 en cliquant sur le bouton Click me .
  • Cliquez sur le bouton Show alert .
  • Augmentez la valeur à 5 avant l'expiration du délai.


Augmentation de la valeur de comptage après avoir cliqué sur le bouton Afficher l'alerte

Que pensez-vous qui apparaît dans la boîte de message? 5 sera-t-il affiché, ce qui correspond à la valeur de count au moment du déclenchement de la minuterie, ou 3 - c'est-à-dire la valeur de count au moment où le bouton est enfoncé?

Vous trouverez maintenant la réponse à cette question, mais si vous voulez tout savoir par vous-même - voici une version de travail de cet exemple.

Si ce que vous avez vu vous semble incompréhensible - voici un exemple plus proche de la réalité. Imaginez une application de chat dans laquelle, dans l'état, l' ID destinataire actuel du message est stocké, et il y a un bouton Send . Dans ce document, ce qui se passe est considéré en détail. En fait, la bonne réponse à la question de ce qui apparaît dans la boîte de message est 3.

Le mécanisme d'affichage d'une boîte de message «capture» l'état au moment du clic sur le bouton.

Il existe des moyens d'implémenter une autre version du comportement, mais pour l'instant nous allons traiter du comportement standard du système. Lors de la construction de modèles mentaux de technologies, il est important de distinguer le «chemin de moindre résistance» de toutes sortes de «sorties de secours».

Comment ça marche?

Nous avons déjà dit que la valeur de count est une constante pour chaque appel spécifique à notre fonction. Je pense que cela vaut la peine de s’étendre plus en détail. Le fait est que notre fonction est appelée plusieurs fois (une fois pour chaque opération de rendu), mais avec chacun de ces appels, count intérieur est une constante. Cette constante est définie sur une valeur spécifique (représentant l'état d'une opération de rendu particulière).

Ce comportement des fonctions n'est pas quelque chose de spécial pour React - les fonctions ordinaires se comportent de la même manière:

 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); 

Dans cet exemple, la variable externe someone réaffectée plusieurs fois. La même chose peut se produire quelque part à l'intérieur de React, l'état actuel du composant peut changer. Cependant, à l'intérieur de la fonction sayHi , il existe un name constante locale associé à la person d'un appel particulier. Cette constante est locale, donc ses valeurs dans différents appels de fonction sont isolées les unes des autres! Par conséquent, après une temporisation, chaque fenêtre de message affichée «se souvient» de sa propre valeur de name .

Cela explique comment notre gestionnaire d'événements capture la valeur de count lorsqu'un bouton est cliqué. Si nous, en travaillant avec des composants, appliquons le même principe, il s'avère que chaque rendu «voit» sa propre valeur de count :

 //     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); } // ... } 

Par conséquent, chaque rendu renvoie en fait son propre handleAlertClick "version". Chacune de ces versions «se souvient» de sa propre valeur de 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 // ... } 

C'est pourquoi dans cet exemple, les gestionnaires d'événements «appartiennent» à des rendus spécifiques, et lorsque vous cliquez sur le bouton, le composant utilise l'état de count de ces rendus.

Dans chaque rendu particulier, les propriétés et l'état restent toujours les mêmes. Mais si différentes opérations de rendu utilisent leurs propres propriétés et états, la même chose se produit avec tous les mécanismes qui les utilisent (y compris les gestionnaires d'événements). Ils "appartiennent" également à des rendus spécifiques. Par conséquent, même les fonctions asynchrones dans les gestionnaires d'événements «verront» les mêmes valeurs de count .

Il convient de noter que dans l'exemple ci-dessus, j'ai intégré les valeurs de count spécifiques directement dans la fonction handleAlertClick . Ce remplacement "mental" ne nous fera pas de mal, car le count constant ne peut pas être modifié dans un rendu particulier. Premièrement, c'est une constante, et deuxièmement, c'est un nombre. On peut dire avec confiance que l'on peut aussi penser à d'autres significations, comme les objets, mais seulement si l'on accepte en règle générale de ne pas faire de changements (mutations) de l'état. Dans le même temps, nous sommes satisfaits de l'appel à setSomething(newObj) avec un nouvel objet au lieu de changer celui existant, car avec cette approche, l'état appartenant au rendu précédent est intact.

Chaque rendu a ses propres effets.


Ce matériel, comme vous le savez, est consacré aux effets, mais nous n'en avons même pas encore parlé. Maintenant, nous allons le réparer. Il s'avère que travailler avec des effets n'est pas particulièrement différent de ce que nous avons déjà compris.

Prenons un exemple tiré de la documentation, qui est très similaire à celui que nous avons déjà analysé:

 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> ); } 

Maintenant, j'ai une question pour vous. Comment un effet lit-il la valeur de count la plus récente?

Peut-être qu'une «liaison de données» est utilisée ici, ou un «objet observateur» qui met à jour la valeur de count à l'intérieur de la fonction d'effet? Peut-être que count est une variable mutable dont la valeur définie par React à l'intérieur de notre composant, à la suite de laquelle l'effet voit toujours sa dernière version?

Non.

Nous savons déjà que dans le rendu d'un composant particulier, le count est une constante. Même les gestionnaires d'événements "voient" la valeur de count du rendu auquel ils "appartiennent" car le count est une constante située dans une certaine étendue. Il en va de même pour les effets!

Et il convient de noter que ce n'est pas le count variables count qui change en quelque sorte à l'intérieur de l'effet "inchangé". Devant nous se trouve la fonction de l'effet lui-même, qui est différent dans chaque opération de rendu.

Chaque version «voit» la valeur de count du rendu auquel elle «appartient»:

 //     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:

  • . . , , - 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:

  • . . , , - 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 . (, - ), .

, , , . ( ).

?

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 setIntervalstep . , , , ! , , , , , .

, , , 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 , . , , .




.

, ?


. , , . , , .

? , . : 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() {   // ... } } 

, : « , , , useEffectcomponentDidMount 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} , , . , , , . Et c'est faux.

, , . — , , 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 , , , - . , , , . , , , , .

Résumé


, . , , - , , , .

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


All Articles