Dans cet article nous allons nous intéresser aux Hooks de React. Disponibles depuis la version 16.8, les Hooks nous permettent de bénéficier d’un état local et d’autres fonctionnalités de React sans avoir à écrire une classe. Ce sont des fonctions qui permettent de « se brancher » sur la gestion d’état local et de cycle de vie de React depuis des fonctions composants. Ils sont complètement optionnels et 100% rétro-compatibles.
Pourquoi?
- Réutiliser la logique à état sans modifier la hiérarchie des composants.
- Réduire la complexité des composants via un découpage en petites fonctions basées sur des parties qui sont intrinsèquement liées.
- Améliore la lisibilité et la compréhension du code.
Hook à état (useState)
Ce hook permet de créer un composant fonctionnel stateful. Voyons comment on peut l’utiliser pour ajouter un état interne à un composant fonctionnel.
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import React, { useState } from 'react'; function Example() { // Déclaration d'une nouvelle variable d'état, que l'on appellera “count” const [count, setCount] = useState(0); return ( <div> <p>Vous avez cliqué {count} fois</p> <button onClick={() => setCount(count + 1)}> Cliquez ici </button> </div> ); }
Dans l'exemple ci-dessus, on utilise le hook useState pour initialiser l'état de notre composant en précisant en argument la valeur de départ.
useState retourne une paire : la valeur de l'état actuel et une fonction qui permet de la modifier. La fonction setCountest similaire à this.setState dans une classe.
À noter qu'on peut combiner plusieurs hooks d'état dans un seul composant :
Copier le code
1 2 3 4 5 6 7 function ExampleWithManyStates() { // Déclaration de multiples variables d'état ! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banane'); const [todos, setTodos] = useState([{ text: 'Apprendre les Hooks' }]); // ... }
Grâce à la déstructuration positionnelle on peut définir des noms distincts aux variables d'état que nous déclarons en appelant useState.
Hook d'effet (useEffect)
Ce hook permet aux fonctions composant de gérer des effets de bord comme nous le faisions avec les méthodes componentDidMount, componentDidUpdate et componentWillUnmount dans les classes.
Ajoutons par exemple une fonctionnalité à notre exemple : lorsque la valeur count est mise à jour, on souhaite mettre à jour le titre de la page :
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Équivalent à componentDidMount plus componentDidUpdate : useEffect(() => { // Mettre à jour le titre du document en utilisant l'API du navigateur document.title = `Vous avez cliqué ${count} fois`; }); return ( ... ) }
Par défaut, React exécute les effets après chaque affichage, y compris le premier. Toutefois, on peut contrôler l'exécution d'un effet grâce au deuxième argument de la fonction.
Copier le code
1 2 3 4 5 6 7 8 9 ... useEffect(() => { // Mettre à jour le titre du document en utilisant l'API du navigateur document.title = `Vous avez cliqué ${count} fois`; }, [count]); return ( ... )
Dans l'exemple ci-dessus nous disons au programme d'appliquer la mise à jour du titre uniquement si la valeur de la variable count a changée. Bien que dans cet exemple il est inutile de préciser ce comportement car il est défini par défaut, dans d'autres cas il peut s'avérer être très utile lorsqu'on travaille avec plusieurs variables.
Les effets peuvent aussi préciser comment « nettoyer » les composants en renvoyant une fonction. Par exemple, ce composant utilise un effet pour s’abonner au statut de connexion d’un ami, et se nettoie en résiliant l’abonnement :
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); // Mise à jour du titre useEffect(() => { document.title = `Vous avez cliqué ${count} fois`; }); const [isOnline, setIsOnline] = useState(null); // Gérer l'abonnement au service useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Se désabonner du service (équivalent à componentWillUnmount) return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ... }
Dans cet exemple, on se désabonne du service lorsque le composant est démonté. Tout comme avec useState, il est possible d'utiliser plusieurs effets d'un composant. Ainsi, on peut organiser les effets de bord dans un composant en rassemblant leurs parties.
Construire vos propres Hooks
Nous allons créer un hook permettant nous abonner au statut d'un ami. Notez que par convention les hooks ont un nom préfixé par use :
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import React, { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }
On peut ensuite utiliser notre hook comme n'importe quel autre hook :
Copier le code
1 2 3 4 5 6 7 8 function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
Les autres Hooks
On a vu les deux hooks les plus importants, mais la nouvelle API propose d'autres hooks très utiles.
useContext
Ce hook permet d'éviter de passer de nombreuses props à travers les composants. On peut par exemple utilises les context en remplacement de redux si la logique reste basique.
useReducer
Similaire à Redux, ce hook retourne un state et une fonction dispatch pour modifier votre store. Il peut être très utile quand vous avez une logique d'état local complexe qui comprend plusieurs sous-valeurs.
useCallback
Ce hook permet de toujours passer la même instance de votre fonction si les paramètres ne changent pas. C'est utile pour passer des fonctions de rappel à des composants enfants sur une égalité référentielle pour éviter des rendus superflus.
useMemo
Ce hook permet de mémoriser le résultat d'une fonction tant que les valeurs d'entrée n'ont pas changées. Cela permet d'optimiser les performances en évitant de recalculer le résultat à chaque appel de fonction.
useRef
Ce hook permet de définir une référence dans un composant fonctionnel, pour par exemple focus un champ.
Les hooks sont encore une fonctionnalité très récente de React mais qui sont de plus en plus adoptés par les développeurs. L'intérêt est de garder une base de code aisément maintenable et facile à comprendre. De plus, comme nous l'avions vus ça permet également d'optimiser les performances de notre application.