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/YodaIl 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;
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:
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'alerteQue 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
:
Par conséquent, chaque rendu renvoie en fait son propre
handleAlertClick
"version". Chacune de ces versions «se souvient» de sa propre valeur de
count
:
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»:
React , DOM .
, ( ), , , , «» , «».
, , .
, ( ,
). , , , .
, , :
React:
:
- :
<p>You clicked 0 times</p>
. - , , :
() => { document.title = 'You clicked 0 times' }
.
React:
:
React:
- , , .
() => { document.title = 'You clicked 0 times' }
.
, . , , - :
:
React:
:
- :
<p>You clicked 1 times</p>
. - , , :
() => { document.title = 'You clicked 1 times' }
.
React:
:
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); });
, «» . ! . , .
, , - , , , , . ,
ref
,
.
, , , , , . , ( ), «» React-. , , . , .
, , , :
function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => {
- 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 ) , ».
! « » , . , , :
, , … , «» , -,
{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';
- ? , , .
, , - :
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 , , . ( .
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 — . , , , ,
useEffect
, , , . ( !)
function SearchResults() { async function fetchData() {
FAQ , . .
« !», — . : , , . , , , — , .
, , . , , , , . , . .
, , .
, React
, , React , .
useEffect(() => { document.title = 'Hello, ' + name; }, [name]);
—, , ,
[]
, , , , :
useEffect(() => { document.title = 'Hello, ' + 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)
:
React, , , — .
count
— , ( ):
const count =
. React .
,. , , React , , . —
- .
React , . , , , .
, , , .
count
:
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]);
. , , — , .
count
,
count
,
setCount(count + 1)
:
,
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' });
.
: « , ?». , 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 SearchResults() { const [query, setQuery] = useState('react');
, (, ), . .
, . , :
function SearchResults() {
.
? , « ». 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
— , .
, «» , .
getFetchUrl
(, , ), :
function SearchResults() {
,
getFetchUrl
. , — . - , , . , , , .
— .
, , :
, . , , .
. ,
useCallback :
function SearchResults() {
useCallback
. : , -, , , .
, . (
'react'
'redux'
). , , ,
query
. , ,
query
,
getFetchUrl
.
,
query
useCallback
:
function SearchResults() { const [query, setQuery] = useState('react'); const getFetchUrl = useCallback(() => {
useCallback
query
, ,
getFetchUrl
,
query
:
function SearchResults() { const [query, setQuery] = useState('react');
useCallback
,
query
,
getFetchUrl
, , .
query
,
getFetchUrl
, . Excel: - , , , .
— , . , :
function Parent() { const [query, setQuery] = useState('react');
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;
, : « , , ,
useEffect
—
componentDidMount
componentDidUpdate
. !».
componentDidUpdate
:
class Child extends Component { state = { data: null }; componentDidMount() { this.props.fetchData(); } componentDidUpdate(prevProps) {
,
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;
, , - , , .
, , .
this
, . , , , , - . ,
this.props.fetchData
, , , , , .
-
useCallback
. , , , . , .
useCallback
props.fetchData
.
,
useMemo
:
function ColorPicker() {
,
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é
, . , , - , , , .
