Académique Documents
Professionnel Documents
Culture Documents
d'effet
Les crochets sont un nouvel ajout dans React 16.8. Ils vous
permettent d'utiliser l'état et d'autres fonctionnalités de React
sans écrire de classe.
Le crochet d'effet vous permet d'effectuer des effets secondaires dans les composants
fonctionnels :
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
Cet extrait est basé sur le contre-exemple de la page précédente , mais nous y avons
ajouté une nouvelle fonctionnalité : nous avons défini le titre du document sur un message
personnalisé incluant le nombre de clics.
La récupération de données, la configuration d'un abonnement et la modification manuelle
du DOM dans les composants React sont tous des exemples d'effets secondaires. Que vous
ayez ou non l'habitude d'appeler ces opérations « effets secondaires » (ou simplement
« effets »), vous les avez probablement déjà effectuées dans vos composants auparavant.
Pointe
Si vous connaissez les méthodes de cycle de vie de la classe React, vous pouvez
considérer useEffectHook
comme componentDidMount, componentDidUpdateet componentWillUnmountcombinés.
Il existe deux types d'effets secondaires courants dans les composants React : ceux qui ne
nécessitent pas de nettoyage et ceux qui le nécessitent. Examinons cette distinction plus en
détail.
Effets sans nettoyage
Parfois, nous souhaitons exécuter du code supplémentaire après que React a mis à jour
le DOM. Les requêtes réseau, les mutations manuelles du DOM et la journalisation sont
des exemples courants d'effets qui ne nécessitent pas de nettoyage. Nous disons cela
parce que nous pouvons les exécuter et les oublier immédiatement. Comparons comment
les classes et les crochets nous permettent d'exprimer ces effets secondaires.
Exemple d'utilisation de classes
Dans les composants de classe React, la renderméthode elle-même ne devrait pas
provoquer d'effets secondaires. Ce serait trop tôt - nous voulons généralement effectuer
nos effets après que React a mis à jour le DOM.
C'est pourquoi dans les cours React, nous mettons des effets secondaires
dans componentDidMountet componentDidUpdate. Pour en revenir à notre exemple, voici un
composant de classe compteur React qui met à jour le titre du document juste après que
React ait apporté des modifications au DOM :
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
Notez comment nous devons dupliquer le code entre ces deux méthodes de cycle de
vie en classe.
En effet, dans de nombreux cas, nous souhaitons effectuer le même effet secondaire, que
le composant vienne d'être monté ou qu'il ait été mis à jour. Conceptuellement, nous
voulons que cela se produise après chaque rendu, mais les composants de la classe React
n'ont pas de méthode comme celle-ci. Nous pourrions extraire une méthode distincte,
mais nous aurions encore à l'appeler à deux endroits.
Voyons maintenant comment nous pouvons faire la même chose avec le useEffectHook.
Exemple d'utilisation de crochets
Nous avons déjà vu cet exemple en haut de cette page, mais regardons-le de plus près :
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
Que fait useEffect-il ? En utilisant ce Hook, vous dites à React que votre composant doit
faire quelque chose après le rendu. React se souviendra de la fonction que vous avez
passée (nous l'appellerons notre "effet") et l'appellera plus tard après avoir effectué les
mises à jour du DOM. À cet effet, nous définissons le titre du document, mais nous
pourrions également effectuer une récupération de données ou appeler une autre API
impérative.
Pourquoi est- useEffectil appelé à l'intérieur d'un composant ? Placer useEffectà
l'intérieur du composant nous permet d'accéder à la countvariable d'état (ou à tout
accessoire) directement depuis l'effet. Nous n'avons pas besoin d'une API spéciale pour le
lire - c'est déjà dans la portée de la fonction. Les crochets englobent les fermetures
JavaScript et évitent d'introduire des API spécifiques à React là où JavaScript fournit déjà
une solution.
S'exécute-t-il useEffectaprès chaque rendu ? Oui! Par défaut, il s'exécute à la fois après
le premier rendu et après chaque mise à jour. (Nous parlerons plus tard de la façon de
personnaliser cela .) Au lieu de penser en termes de "montage" et de "mise à jour", vous
trouverez peut-être plus facile de penser que les effets se produisent "après le
rendu". React garantit que le DOM a été mis à jour au moment où il exécute les effets.
Explication détaillée
Maintenant que nous en savons plus sur les effets, ces lignes devraient avoir un sens :
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
}
Nous déclarons la countvariable d'état, puis nous disons à React que nous devons utiliser un
effet. Nous passons une fonction au useEffectHook. Cette fonction que nous
passons est notre effet. Dans notre effet, nous définissons le titre du document à l'aide de
l' document.titleAPI du navigateur. Nous pouvons lire la dernière countà l'intérieur de l'effet
car c'est dans le cadre de notre fonction. Lorsque React rend notre composant, il se
souviendra de l'effet que nous avons utilisé, puis exécutera notre effet après la mise à jour
du DOM. Cela se produit pour chaque rendu, y compris le premier.
Les développeurs JavaScript expérimentés peuvent remarquer que la fonction transmise
à useEffectsera différente sur chaque rendu. C'est intentionnel. En fait, c'est ce qui nous
permet de lire la countvaleur depuis l'intérieur de l'effet sans craindre qu'il ne devienne
obsolète. Chaque fois que nous effectuons un nouveau rendu, nous programmons un
effet différent , remplaçant le précédent. D'une certaine manière, cela fait que les effets se
comportent davantage comme une partie du résultat du rendu - chaque effet "appartient"
à un rendu particulier. Nous verrons plus clairement pourquoi cela est utile plus loin sur
cette page .
Pointe
Contrairement à componentDidMountou componentDidUpdate, les effets planifiés
avec useEffectn'empêchent pas le navigateur de mettre à jour l'écran. Cela rend votre
application plus réactive. La majorité des effets n'ont pas besoin de se produire de manière
synchrone. Dans les cas rares où ils le font (comme la mesure de la mise en page), il existe
un useLayoutEffectcrochet séparé avec une API identique à useEffect.
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
résumer
Nous avons appris que cela useEffectnous permet d'exprimer différents types d'effets
secondaires après le rendu d'un composant. Certains effets peuvent nécessiter un
nettoyage afin qu'ils renvoient une fonction :
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
Le crochet d'effet unifie les deux cas d'utilisation avec une seule API.
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
// ...
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}
Les crochets nous permettent de diviser le code en fonction de ce qu'il fait plutôt que
d'un nom de méthode de cycle de vie. React appliquera tous les effets utilisés par le
composant, dans l'ordre où ils ont été spécifiés.
Explication : Pourquoi les effets s'exécutent-ils à chaque
mise à jour ?
Si vous êtes habitué aux classes, vous vous demandez peut-être pourquoi la phase de
nettoyage des effets se produit après chaque nouveau rendu, et pas une seule fois lors du
démontage. Regardons un exemple pratique pour voir pourquoi cette conception nous
aide à créer des composants avec moins de bugs.
Plus tôt sur cette page , nous avons introduit un exemple FriendStatusde composant qui
indique si un ami est en ligne ou non. Notre classe lit friend.idà partir de this.props,
s'abonne au statut d'ami après le montage du composant et se désabonne pendant le
démontage :
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
Mais que se passe-t-il si l' friendaccessoire change alors que le composant est à
l'écran ? Notre composant continuerait à afficher le statut en ligne d'un ami différent. C'est
un bogue. Nous provoquerions également une fuite de mémoire ou un plantage lors du
démontage, car l'appel de désabonnement utiliserait le mauvais identifiant d'ami.
Dans un composant de classe, nous aurions besoin d'ajouter componentDidUpdatepour gérer
ce cas :
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
// Unsubscribe from the previous friend.id
ChatAPI.unsubscribeFromFriendStatus(prevProps.friend.id, this.handleStatusChange);
// Subscribe to the next friend.id
ChatAPI.subscribeToFriendStatus(this.props.friend.id, this.handleStatusChange);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
Il ne souffre pas de ce bug. (Mais nous n'y avons pas non plus apporté de modifications.)
Il n'y a pas de code spécial pour gérer les mises à jour car useEffectles gère par défaut . Il
nettoie les effets précédents avant d'appliquer les effets suivants. Pour illustrer cela, voici
une séquence d'appels d'abonnement et de désabonnement que ce composant pourrait
produire au fil du temps :
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
Ce comportement garantit la cohérence par défaut et évite les bogues courants dans les
composants de classe en raison d'une logique de mise à jour manquante.
Astuce : Optimiser les performances en sautant les effets
Dans certains cas, le nettoyage ou l'application de l'effet après chaque rendu peut créer un
problème de performances. Dans les composants de classe, nous pouvons résoudre ce
problème en écrivant une comparaison supplémentaire avec prevPropsou prevStateà
l'intérieur componentDidUpdate:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
Cette exigence est suffisamment courante pour être intégrée à l' useEffectAPI Hook. Vous
pouvez demander à React d' ignorer l'application d'un effet si certaines valeurs n'ont pas
changé entre les rendus. Pour cela, passez un tableau en second argument optionnel
à useEffect:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
Dans l'exemple ci-dessus, nous passons [count]comme deuxième argument. Qu'est-ce que
ça veut dire? Si le countest 5, et que notre composant est restitué avec counttoujours égal
à 5, React comparera [5]le rendu précédent et [5]le rendu suivant. Étant donné que tous les
éléments du tableau sont identiques ( 5 === 5), React ignorerait l'effet. C'est notre
optimisation.
Lorsque nous rendons avec countupdated to 6, React comparera les éléments du [5]tableau
du rendu précédent aux éléments du [6]tableau du prochain rendu. Cette fois, React
réappliquera l'effet car 5 !== 6. S'il y a plusieurs éléments dans le tableau, React réexécutera
l'effet même si un seul d'entre eux est différent.
Cela fonctionne également pour les effets qui ont une phase de nettoyage :
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // Only re-subscribe if props.friend.id changes
Prochaines étapes
Toutes nos félicitations! C'était une longue page, mais j'espère qu'à la fin, la plupart de vos
questions sur les effets ont trouvé une réponse. Vous avez appris à la fois le crochet d'état
et le crochet d'effet, et vous pouvez faire beaucoup de choses avec les deux combinés. Ils
couvrent la plupart des cas d'utilisation des classes - et là où ils ne le font pas, vous
pourriez trouver les crochets supplémentaires utiles.
Nous commençons également à voir comment les crochets résolvent les problèmes décrits
dans Motivation . Nous avons vu comment le nettoyage des effets évite la duplication
dans componentDidUpdateet componentWillUnmount, rapproche le code associé et nous aide à
éviter les bogues. Nous avons également vu comment nous pouvons séparer les effets en
fonction de leur objectif, ce que nous ne pouvions pas du tout faire en classe.
À ce stade, vous vous demandez peut-être comment fonctionnent les crochets. Comment
React peut-il savoir quel useStateappel correspond à quelle variable d'état entre les
rendus ? Comment React "fait-il correspondre" les effets précédents et suivants sur chaque
mise à jour ? Sur la page suivante, nous découvrirons les règles des crochets - elles
sont essentielles pour faire fonctionner les crochets.