Libérez la puissance de la logique réutilisable dans vos applications React avec les hooks personnalisés. Apprenez à créer et utiliser des hooks personnalisés pour un code plus propre et plus facile à maintenir.
Hooks Personnalisés : Patrons de Logique Réutilisable dans React
Les hooks de React ont révolutionné la façon dont nous écrivons les composants React en introduisant l'état et les fonctionnalités de cycle de vie aux composants fonctionnels. Parmi les nombreux avantages qu'ils offrent, les hooks personnalisés se distinguent comme un mécanisme puissant pour extraire et réutiliser la logique entre plusieurs composants. Cet article de blog explorera en profondeur le monde des hooks personnalisés, en examinant leurs avantages, leur création et leur utilisation avec des exemples pratiques.
Que sont les Hooks Personnalisés ?
Essentiellement, un hook personnalisé est une fonction JavaScript qui commence par le mot « use » et peut appeler d'autres hooks. Ils vous permettent d'extraire la logique d'un composant dans des fonctions réutilisables. C'est un moyen puissant de partager une logique avec état (stateful), des effets de bord ou d'autres comportements complexes entre les composants sans recourir aux render props, aux composants d'ordre supérieur (HOC) ou à d'autres patrons complexes.
Caractéristiques Clés des Hooks Personnalisés :
- Convention de Nommage : Les hooks personnalisés doivent commencer par le mot « use ». Cela signale à React que la fonction contient des hooks et doit suivre les règles des hooks.
- Réutilisabilité : L'objectif principal est d'encapsuler une logique réutilisable, facilitant ainsi le partage de fonctionnalités entre les composants.
- Logique avec État : Les hooks personnalisés peuvent gérer leur propre état en utilisant le hook
useState
, ce qui leur permet d'encapsuler des comportements complexes avec état. - Effets de Bord : Ils peuvent également effectuer des effets de bord en utilisant le hook
useEffect
, permettant l'intégration avec des API externes, la récupération de données, et plus encore. - Composabilité : Les hooks personnalisés peuvent appeler d'autres hooks, vous permettant de construire une logique complexe en composant des hooks plus petits et plus ciblés.
Avantages de l'Utilisation des Hooks Personnalisés
Les hooks personnalisés offrent plusieurs avantages significatifs dans le développement React :
- Réutilisabilité du Code : L'avantage le plus évident est la capacité de réutiliser la logique entre plusieurs composants. Cela réduit la duplication de code et favorise une base de code plus DRY (Don't Repeat Yourself).
- Lisibilité Améliorée : En extrayant la logique complexe dans des hooks personnalisés distincts, vos composants deviennent plus propres et plus faciles à comprendre. La logique principale du composant reste concentrée sur le rendu de l'interface utilisateur.
- Maintenabilité Améliorée : Lorsque la logique est encapsulée dans des hooks personnalisés, les modifications et les corrections de bugs peuvent être appliquées à un seul endroit, réduisant le risque d'introduire des erreurs dans plusieurs composants.
- Testabilité : Les hooks personnalisés peuvent être facilement testés de manière isolée, garantissant que la logique réutilisable fonctionne correctement indépendamment des composants qui l'utilisent.
- Composants Simplifiés : Les hooks personnalisés aident à désencombrer les composants, les rendant moins verbeux et plus concentrés sur leur objectif principal.
Créer Votre Premier Hook Personnalisé
Illustrons la création d'un hook personnalisé avec un exemple pratique : un hook qui suit la taille de la fenêtre.
Exemple : useWindowSize
Ce hook retournera la largeur et la hauteur actuelles de la fenêtre du navigateur. Il mettra également à jour ces valeurs lorsque la fenêtre sera redimensionnée.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// Supprimer l'écouteur d'événement au nettoyage
return () => window.removeEventListener('resize', handleResize);
}, []); // Le tableau vide assure que l'effet ne s'exécute qu'au montage
return windowSize;
}
export default useWindowSize;
Explication :
- Importer les Hooks Nécessaires : Nous importons
useState
etuseEffect
de React. - Définir le Hook : Nous créons une fonction nommée
useWindowSize
, en respectant la convention de nommage. - Initialiser l'État : Nous utilisons
useState
pour initialiser l'étatwindowSize
avec la largeur et la hauteur initiales de la fenêtre. - Mettre en Place un Écouteur d'Événement : Nous utilisons
useEffect
pour ajouter un écouteur d'événement de redimensionnement à la fenêtre. Lorsque la fenêtre est redimensionnée, la fonctionhandleResize
met à jour l'étatwindowSize
. - Nettoyage : Nous retournons une fonction de nettoyage depuis
useEffect
pour supprimer l'écouteur d'événement lorsque le composant est démonté. Cela prévient les fuites de mémoire. - Retourner les Valeurs : Le hook retourne l'objet
windowSize
, contenant la largeur et la hauteur actuelles de la fenêtre.
Utiliser le Hook Personnalisé dans un Composant
Maintenant que nous avons créé notre hook personnalisé, voyons comment l'utiliser dans un composant React.
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
Largeur de la fenêtre : {width}px
Hauteur de la fenêtre : {height}px
);
}
export default MyComponent;
Explication :
- Importer le Hook : Nous importons le hook personnalisé
useWindowSize
. - Appeler le Hook : Nous appelons le hook
useWindowSize
à l'intérieur du composant. - Accéder aux Valeurs : Nous déstructurons l'objet retourné pour obtenir les valeurs
width
etheight
. - Afficher les Valeurs : Nous affichons les valeurs de largeur et de hauteur dans l'interface utilisateur du composant.
Tout composant utilisant useWindowSize
se mettra automatiquement à jour lorsque la taille de la fenêtre changera.
Exemples Plus Complexes
Explorons quelques cas d'utilisation plus avancés pour les hooks personnalisés.
Exemple : useLocalStorage
Ce hook vous permet de stocker et de récupérer facilement des données du stockage local.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// État pour stocker notre valeur
// Passer la valeur initiale à useState pour que la logique ne soit exécutée qu'une fois
const [storedValue, setStoredValue] = useState(() => {
try {
// Obtenir depuis le stockage local par clé
const item = window.localStorage.getItem(key);
// Analyser le JSON stocké ou retourner initialValue s'il n'y en a pas
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// En cas d'erreur, retourner également initialValue
console.log(error);
return initialValue;
}
});
// Retourner une version encapsulée de la fonction setter de useState qui...
// ... persiste la nouvelle valeur dans localStorage.
const setValue = (value) => {
try {
// Permettre à la valeur d'être une fonction pour avoir la même API que useState
const valueToStore = value instanceof Function ? value(storedValue) : value;
// Sauvegarder dans le stockage local
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// Sauvegarder l'état
setStoredValue(valueToStore);
} catch (error) {
// Une implémentation plus avancée gérerait le cas d'erreur
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
Utilisation :
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Invité');
return (
Bonjour, {name} !
setName(e.target.value)}
/>
);
}
export default MyComponent;
Exemple : useFetch
Ce hook encapsule la logique de récupération de données depuis une API.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Erreur HTTP ! statut : ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Utilisation :
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return Chargement...
;
if (error) return Erreur : {error.message}
;
return (
Titre : {data.title}
Terminé : {data.completed ? 'Oui' : 'Non'}
);
}
export default MyComponent;
Meilleures Pratiques pour les Hooks Personnalisés
Pour garantir que vos hooks personnalisés sont efficaces et maintenables, suivez ces meilleures pratiques :
- Gardez-les Ciblés : Chaque hook personnalisé doit avoir un seul objectif bien défini. Évitez de créer des hooks trop complexes qui essaient d'en faire trop.
- Documentez Vos Hooks : Fournissez une documentation claire et concise pour chaque hook personnalisé, expliquant son objectif, ses entrées et ses sorties.
- Testez Vos Hooks : Rédigez des tests unitaires pour vos hooks personnalisés afin de vous assurer qu'ils fonctionnent correctement et de manière fiable.
- Utilisez des Noms Descriptifs : Choisissez des noms descriptifs pour vos hooks personnalisés qui indiquent clairement leur objectif.
- Gérez les Erreurs avec Élégance : Implémentez la gestion des erreurs dans vos hooks personnalisés pour éviter les comportements inattendus et fournir des messages d'erreur informatifs.
- Pensez à la Réutilisabilité : Concevez vos hooks personnalisés en gardant la réutilisabilité à l'esprit. Rendez-les suffisamment génériques pour être utilisés dans plusieurs composants.
- Évitez la Sur-Abstraction : Ne créez pas de hooks personnalisés pour une logique simple qui peut être facilement gérée au sein d'un composant. N'extrayez que la logique qui est vraiment réutilisable et complexe.
Pièges Courants à Éviter
- Enfreindre les Règles des Hooks : Appelez toujours les hooks au niveau supérieur de votre fonction de hook personnalisé et ne les appelez que depuis des composants fonctionnels React ou d'autres hooks personnalisés.
- Ignorer les Dépendances dans useEffect : Assurez-vous d'inclure toutes les dépendances nécessaires dans le tableau de dépendances du hook
useEffect
pour éviter les fermetures (closures) obsolètes et les comportements inattendus. - Créer des Boucles Infinies : Soyez prudent lorsque vous mettez à jour l'état dans un hook
useEffect
, car cela peut facilement conduire à des boucles infinies. Assurez-vous que la mise à jour est conditionnelle et basée sur les changements de dépendances. - Oublier le Nettoyage : Incluez toujours une fonction de nettoyage dans
useEffect
pour supprimer les écouteurs d'événements, annuler les abonnements et effectuer d'autres tâches de nettoyage pour éviter les fuites de mémoire.
Patrons Avancés
Composer des Hooks Personnalisés
Les hooks personnalisés peuvent être composés ensemble pour créer une logique plus complexe. Par exemple, vous pourriez combiner un hook useLocalStorage
avec un hook useFetch
pour persister automatiquement les données récupérées dans le stockage local.
Partager la Logique entre les Hooks
Si plusieurs hooks personnalisés partagent une logique commune, vous pouvez extraire cette logique dans une fonction utilitaire distincte et la réutiliser dans les deux hooks.
Utiliser le Contexte avec les Hooks Personnalisés
Les hooks personnalisés peuvent être utilisés en conjonction avec le Contexte de React pour accéder et mettre à jour l'état global. Cela vous permet de créer des composants réutilisables qui sont conscients de l'état global de l'application et peuvent interagir avec lui.
Exemples du Monde Réel
Voici quelques exemples de la façon dont les hooks personnalisés peuvent être utilisés dans des applications du monde réel :
- Validation de Formulaire : Créez un hook
useForm
pour gérer l'état, la validation et la soumission du formulaire. - Authentification : Implémentez un hook
useAuth
pour gérer l'authentification et l'autorisation des utilisateurs. - Gestion de Thème : Développez un hook
useTheme
pour basculer entre différents thèmes (clair, sombre, etc.). - Géolocalisation : Construisez un hook
useGeolocation
pour suivre la position actuelle de l'utilisateur. - Détection de Défilement : Créez un hook
useScroll
pour détecter lorsque l'utilisateur a défilé jusqu'à un certain point de la page.
Exemple : hook useGeolocation pour les applications interculturelles comme les services de cartographie ou de livraison
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'La géolocalisation n\'est pas prise en charge par ce navigateur.',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
Conclusion
Les hooks personnalisés sont un outil puissant pour écrire du code React plus propre, plus réutilisable et plus maintenable. En encapsulant la logique complexe dans des hooks personnalisés, vous pouvez simplifier vos composants, réduire la duplication de code et améliorer la structure globale de vos applications. Adoptez les hooks personnalisés et libérez leur potentiel pour construire des applications React plus robustes et évolutives.
Commencez par identifier les zones de votre base de code existante où la logique est répétée entre plusieurs composants. Ensuite, refactorisez cette logique en hooks personnalisés. Au fil du temps, vous construirez une bibliothèque de hooks réutilisables qui accélérera votre processus de développement et améliorera la qualité de votre code.
N'oubliez pas de suivre les meilleures pratiques, d'éviter les pièges courants et d'explorer les patrons avancés pour tirer le meilleur parti des hooks personnalisés. Avec de la pratique et de l'expérience, vous deviendrez un maître des hooks personnalisés et un développeur React plus efficace.