Cela va bientôt faire un an et demi que les hooks Reacts ont été livrés au monde, pour le meilleur. ❤️ Deux modes de création pour les composant cohabitent depuis lors – les class components et les functional components – et cela peut souvent prêter à confusion pour un débutant ou même un développeur junior. Quelle est la meilleure manière de construire un composant React aujourd’hui ?
Les class component sont morts, vivent les class component (ou pas)
L’utilisation des class component est restée, jusqu’à la v16.8 de React, le seul moyen de faire usage du state React. Le class component était donc le passage inévitable du développeur React voulant créer un composant avec un minimum de complexité fonctionnelle. Mais ce type de composant embarque par défaut de nombreuses méthodes internes ce qui alourdit considérablement le poids du code compilé (cf. cet article qui compare un même composant React créé en class component et en functional component).
Ci-dessous voyez une partie des méthodes internes à la class Component, dont on a pas toujours besoin:
class MonClassComponent extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {}
componentShouldUpdate() {}
componentDidUpdate() {}
componentDidUnmount() {}
// ect...
}
D’autre part, le class component est intrinsèquement lié au cycle de vie des composants React, et c’est un mode de fonctionnement qui est à mon sens plus complexe à intégrer pour les débutants que le mode de fonctionnement associé à l’utilisation des hooks.
L’avènement des Hooks
J’aime les Hooks ! Je ne vais pas m’en cacher, et je pense que vous les aimerez tout autant que moi prochainement ! 😍
Premier point positif: nous pouvons enfin nous débarrasser de la class Component et de toute la complexité qu’elle amenait (constructor, méthodes non utilisées…) 🔥
Deuxième point positif: nous pouvons complètement et définitivement oublier la notion de cycle de vie React !
Mais alors, comment pouvons nous déclencher des actions dans notre composant à des moments clé (mounting ou unmounting par exemple) ?
Prenons par exemple la méthode componentDidMount()
des class components, appelée au moment où le composant a été rendu pour la première fois.
class MonClassComponent extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Mounted !');
}
}
L’exemple ci-dessus figure comment afficher un log indiquant le moment où le composant est mounted selon l’ancien jargon React, ce qui revient à dire rendu pour la première fois. Comment pouvons nous reproduire cet exemple à l’aide des hooks ?
const MonFunctionalComponent = () => {
React.useEffect(() => {
console.log('Rendered for the first time !');
}, []);
};
On voit déjà que le code est plus clair. Dans l’implémentation class component, on va se baser sur la classe React.Component pour créer notre composant, puis ajouter un appel super(props)
puis enfin appeler dans notre méthode componentDidMount notre fonction de log. On a donc 3 étapes avec à priori aucune information sur ce que contient la classe Component. Bof !
Dans l’implémentation en functional component avec un Hook on utilise une fonction javascript dans laquelle on va “brancher” un hook useEffect
. Notre useEffect
va prendre deux paramètres, une fonction, et un array
de dépendances. Pour vous expliquer brièvement car ce sera le sujet d’un autre cours, l’array de dépendances est un array
d’éléments (variable, fonction…) que notre hook va “écouter”. Dans le cas où l’une de ces dépendances venait à changer, la fonction passée en premier paramètre est appelée. Dans notre cas, on ne passe aucune dépendance au hook, et donc la fonction n’est appelée qu’au premier rendu du composant.
Le composant avec Hooks est une simple fonction, sans méthodes inutiles. Il est nettement plus lisible que le class component une fois le fonctionnement du hook useEffect
maitrisé. Hooks FTW !
Et le state dans tout ça ?
Ah ! L’utilisation du state s’améliore également avec le hook useState
. Voyons ensemble un exemple sur la manière d’utiliser le state dans un class component:
class MonClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
classComponentsSuck: false
}
}
componentDidMount() {
this.setState({ classComponentsSuck: true });
}
}
Il faut dans un premier temps déclarer l’objet state
global puis on va le modifier via la méthode setState
.
Avec le hook useState
on ne va plus déclarer d’objet state global mais on va brancher un hook useState
par propriété de state que l’on veut déclarer:
const MonFunctionalComponent = () => {
const [hooksRule, setHooksRule] = React.useState(false);
React.useEffect(() => {
setHooksRule(true);
}, []);
React.useEffect(() => {
console.log('Do hooks rule?', hooksRule);
}, [hooksRule]);
};
Ici on branche donc notre useState
en lui passant la valeur par défaut de notre variable de state. Le hook nous met à disposition notre variable hooksRule
ainsi que la méthode setHooksRule
permettant de modifier la valeur d’état de hooksRule. Ainsi au premier rendu du composant, on va passer hooksRule
à true avec la méthode setHooksRule
. Le deuxième useEffect sert ici d’exemple au dépendances que j’évoquais plus haut: on a créé le useEffect
en le branchant sur hooksRule
, ainsi à chaque fois que hooksRule
est modifié un log est affiché dans la console. On aura donc deux logs:
Do hooks rule? false // lors du premier rendu
Do hooks rule? true // lorsqu'on modifie la valeur d'état de hooksRule
Globalement le code est beaucoup plus clair/lisible que dans le class component au dessus: on a une valeur d’état et une méthode pour la mettre à jour. Basta. J’adore.
Conclusion
Nous avons fait un rapide tour d’horizon des avantages qu’apportent les Hooks React par rapport aux class component. Clarté, lisibilité, simplicité, les avantages sont nombreux et c’est un K.O. pour les class component dans ce match. 😵 Je vous recommande donc de vous mettre au plus tôt aux Hooks, si ce n’est pas déjà fait ! À venir prochainement, comment utiliser le hook useEffect
en détails… Stay tuned ! 👋