
Les deux règles fondamentales des hooks (Appel au niveau supérieur, appel depuis fonctions React)
Détail des deux règles essentielles pour utiliser les Hooks React correctement : appel au niveau supérieur uniquement et appel depuis les fonctions React ou Custom Hooks.
Introduction : La base du fonctionnement des Hooks
Les Hooks ont révolutionné l'écriture des composants React en permettant l'utilisation de l'état et d'autres fonctionnalités React sans écrire de classes. Cependant, cette puissance repose sur un mécanisme interne qui exige que les développeurs respectent deux règles fondamentales lors de l'utilisation des Hooks. Ces règles ne sont pas de simples conventions ; elles sont essentielles pour garantir que React puisse suivre correctement l'état et les effets associés à chaque Hook entre les différents rendus d'un composant.
Enfreindre ces règles peut conduire à des comportements inattendus, des erreurs difficiles à déboguer, et une application instable. Heureusement, elles sont relativement simples à comprendre et à suivre, surtout avec l'aide des outils de linting.
Règle n°1 : Appeler les Hooks uniquement au Niveau Supérieur
L'énoncé de la règle :
N'appelez jamais les Hooks React (useState, useEffect, etc., ou vos propres Hooks personnalisés) à l'intérieur de boucles, de conditions, ou de fonctions imbriquées. Les appels aux Hooks doivent toujours se faire au niveau supérieur du corps de votre fonction composant React (ou de votre Hook personnalisé), avant toute instruction `return` précoce.
Pourquoi cette règle existe-t-elle ?
React maintient une liste interne d'emplacements mémoire (cellules) pour chaque Hook utilisé par un composant. Lors du premier rendu, les Hooks sont appelés dans un certain ordre (useState puis useEffect, par exemple) et React alloue une cellule pour chacun. Lors des rendus suivants, React s'attend à retrouver exactement le même ordre d'appel pour pouvoir restituer l'état correct à chaque Hook. Si vous appelez un Hook conditionnellement (par exemple, dans un `if`), l'ordre des appels peut changer d'un rendu à l'autre, et React perdra le fil, associant potentiellement l'état d'un Hook à un autre.
Exemple à ne pas faire :
function FormulaireConditionnel({ afficherEmail }) {
const [name, setName] = useState(''); // OK: Niveau supérieur
if (afficherEmail) {
// !!! ERREUR !!! Hook appelé dans une condition
const [email, setEmail] = useState('');
}
// Si 'afficherEmail' devient false, l'ordre des hooks change
// useEffect(() => { ... }); // Cet useEffect pourrait recevoir l'état de 'email' par erreur !
return (
);
}
Comment faire correctement :
Appelez toujours vos Hooks au niveau supérieur. Si vous avez besoin d'une logique conditionnelle, placez la condition à l'intérieur du Hook (si son API le permet, comme pour `useEffect`) ou gérez l'affichage conditionnel dans le JSX.
function FormulaireConditionnel({ afficherEmail }) {
const [name, setName] = useState(''); // OK
const [email, setEmail] = useState(''); // OK: Toujours appelé
useEffect(() => {
// Condition à l'intérieur de l'effet
if (afficherEmail) {
console.log("L'email est affiché et sa valeur est :", email);
}
}, [email, afficherEmail]);
return (
);
}
Règle n°2 : Appeler les Hooks uniquement depuis des Fonctions React
L'énoncé de la règle :
N'appelez les Hooks que depuis deux endroits :
- Le corps des composants fonctionnels React.
- Le corps des Hooks personnalisés (Custom Hooks).
Cela signifie que vous ne pouvez pas appeler de Hooks :
- Dans des composants de classe React.
- Dans des fonctions JavaScript ordinaires (qui ne sont ni des composants ni des hooks personnalisés).
- A l'intérieur de gestionnaires d'événements, de `useMemo`, `useReducer`, `useEffect`, ou de toute autre fonction imbriquée.
Pourquoi cette règle existe-t-elle ?
Les Hooks ont besoin d'être associés à un composant spécifique pour que React puisse gérer leur état et leur cycle de vie. Seuls les composants fonctionnels et les Hooks personnalisés fournissent ce contexte nécessaire pendant la phase de rendu. Tenter d'appeler un Hook en dehors de ce contexte n'a pas de sens et mènera à des erreurs.
Exemples à ne pas faire :
// Fonction JS classique
function getDonnees() {
// !!! ERREUR !!! useState ne peut pas être appelé ici
// const [data, setData] = useState(null);
// ...
}
function MonComposant() {
function handleClick() {
// !!! ERREUR !!! useEffect ne peut pas être appelé dans un gestionnaire d'événement
// useEffect(() => { /* ... */ });
}
// OK: useState appelé au niveau supérieur du composant fonctionnel
const [count, setCount] = useState(0);
return ;
}
Où appeler les Hooks (Correct) :
// Dans un composant fonctionnel
function MonComposantValide() {
const [etat, setEtat] = useState(0); // OK
useEffect(() => { /* ... */ }); // OK
const maRef = useRef(); // OK
const monHookPerso = useMonHookPersonnalise(); // OK
// ...
}
// Dans un Hook personnalisé
function useMonHookPersonnalise() {
const [valeur, setValeur] = useState(false); // OK
useEffect(() => { /* ... */ }); // OK
// ...
return valeur;
}
Conclusion : Garantir la magie des Hooks
Ces deux règles fondamentales sont le contrat que les développeurs doivent respecter pour permettre à React d'implémenter la "magie" des Hooks de manière fiable. En appelant systématiquement les Hooks au niveau supérieur et uniquement depuis des fonctions React (composants ou hooks personnalisés), vous garantissez la cohérence de l'ordre d'appel, ce qui est indispensable au bon fonctionnement de l'état et des effets.
Heureusement, vous n'avez pas à vous fier uniquement à votre mémoire. L'utilisation du plugin ESLint eslint-plugin-react-hooks permet de détecter automatiquement la plupart des violations de ces règles, vous aidant ainsi à écrire du code conforme et à éviter des bugs potentiels.