À l'heure où ce cours a été écrit, nous en sommes à la version 17.0.2 de React. React a effectivement connu de nombreuses évolutions. Cela veut aussi dire que la manière dont on écrit un composant aujourd'hui ne ressemble pas du tout à la manière dont on le faisait au début. On pourrait considérer que ce qui appartient au passé reste dans le passé. Mais ce serait sans compter les codebases qui contiennent des composants écrits dans d'autres syntaxes. Remontons dans notre machine à remonter dans le temps pour voir les grandes étapes de React. 🚀
Découvrez les évolutions principales de React
Au tout débuts : createClass
Lorsque React a été rendu open source en 2013, les composants étaient créés avec React.createClass
. Pour vous donner un exemple, un composant MyComponent
qui reçoit une prop title
se serait écrit de la manière suivante :
React.createClass({
displayName: "MyComponent",
render() {
return (
<div>
<h1>{this.props.title}</h1>
</div>
)
}
})
Mais aujourd'hui, cette syntaxe est officiellement considérée comme dépréciée, et vous ne la trouverez nulle part dans la documentation de React.
... En revanche, vous risquez clairement de tomber sur des composants classe.
Le temps des composants classe
Avec l'ajout des classes dans ES 2015 (voici la documentation de Mozilla sur les classes, en anglais), React a voulu s'aligner en créant les composants classe. Il s'agissait à l'époque d'un changement majeur. Durant ce chapitre ainsi que le chapitre suivant, nous allons prendre le temps de revenir sur leur syntaxe, la manière de faire des appels API et de gérer le state et les props dans les composants classe.
Vous êtes invité à découvrir dès maintenant dans le screencast ci-dessous la syntaxe des composants classe. 👇
Si vous voulez voir le code complet de ce composant entier, vous pouvez le trouver juste ici, sur le repository GitHub.
Découvrez la syntaxe
Comme vous avez pu le voir dans le screencast juste au-dessus, la syntaxe des composants classe est différente. Nous allons ici créer un nouveau composant EmailInput
que nous mettrons dans le footer.
On commence d’abord par déclarer le composant :
import { Component } from 'react'
class EmailInput extends Component {
constructor() {
}
render() {
return (
<div>
<input />
</div>
)
}
}
export default EmailInput
class
et extends Component
sont ici la manière de déclarer notre composant. Si vous avez déjà manipulé des classes, vous devriez déjà avoir vu extends
et constructor
. Mais sachez que le constructor
, dont vous trouverez la documentation ici, est une méthode qui est utilisée pour créer et initialiser un objet lorsqu'on utilise le mot cléclass
.
Mais au fait, c’est quoi ce render
?!
Eh bien, il s’agit d’une méthode de votre composant. C'est d'ailleurs la seule méthode qui est obligatoirement appelée dans votre composant (et donc qui doit impérativement y figurer). Pour un certain nombre d'événements, votre composant devra être "re-render". À chaque fois, cette méthode va recalculer ce qui y est déclaré, et afficher ce qui est retourné.
Il y aura un nouveau render à chaque fois qu’une mise à jour aura lieu :
à l’initialisation du
constructor
;à chaque mise à jour d’une props ;
à chaque changement du state (mais nous y reviendrons plus tard).
Effectivement, vous n’avez pas de render
dans les composants fonction.
Notez que render
doit forcément retourner du JSX (ce qui sera dans le return
). Mais si vous n'avez rien à retourner dans votre render
, vous pouvez également retourner null
.
Accédez aux props
Nous allons ajouter un peu de style au composant que nous venons de créer, en faisant une version pour le darkMode
et une version pour le lightMode
. Sauf que, comme je vous l’ai dit dans le chapitre sur le Contexte, utiliser le Contexte est beaucoup plus simple avec les hooks… Or, on ne peut pas utiliser les hooks depuis les composants classe. On va donc se contenter de passer le theme
en props.
Mais d’ailleurs, ici, on n’a pas de paramètres dans lesquels récupérer les props ? On fait comment pour les récupérer ?
Vous vous en doutez sûrement, il existe également un moyen de les récupérer dans les composants classe. Pour cela, vous pouvez utiliser this.props
partout dans votre composant – que ce soit dans le return
, le render
, ou dans une méthode. On va d’abord les initialiser dans le constructor
, on en profite pour lui passer le state
initial, même si nous n’en avons pas besoin pour le moment (sinon, notre linter va râler).
On déclare donc dans notre composant :
class EmailInput extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: '',
}
}
render() {
// Ici on récupère theme en destructurant this.props
const { theme } = this.props
return (
<div>
<input />
</div>
)
}
}
Ce qui va nous permettre de faire un style différent en fonction de notre theme
. Vous pourrez trouver ce style sur P4C1-exercice
.
Mais au fait, c’est quoi ce this
, là ?
Si vous êtes familier de JavaScript, vous connaissez sûrement déjà this
. this
fait référence à l'objet auquel il appartient. Pour rappel, vous pouvez voir comment définir une classe en JavaScript dans le chapitre Définissez des objets et leurs attributs avec des classes du cours Apprenez à programmer avec JavaScript.
Donc, ici, le this
fait référence à notre composant : il s'agit des props
et du state
de EmailInput
.
Mais attention, lorsque vous déclarez des méthodes (qui sont en quelque sorte des fonctions) dans vos composants, il vous faudra faire attention au bind
.
C'est quoi encore ça, le "bind" ?
Déjà, commençons simple : "bind" en traduction littérale veut dire "lier". Cela veut dire qu'on va lier nos méthodes à notre composant classe : il faut qu'elles soient correctement bindées au this
. C'est ce que nous verrons dans quelques instants, une fois que nous aurons utilisé le state
. ✨
Gérez le state avec setState
Nous allons maintenant gérer la valeur qui est saisie dans notre EmailInput
avec setState
.
setState
... C'est un peu la même chose que useState
, non ?
Eh bien… non. Tout d'abord, useState
est un hook : il s'agit donc de la manière de gérer le state pour les composants fonction. setState
concerne les composants classe. Par ailleurs, si ces deux fonctions concernent toutes les deux le state, elles ne fonctionnent pas de la même manière :
useState
vous permet de déclarer votre variable de state, d'initialiser sa valeur et de récupérer une fonction pour la mettre à jour ;mais
setState
permet uniquement de mettre à jour tout le state de notre composant. Souvenez-vous : le state initial est déclaré dans leconstructor
.
Pour gérer le state de la valeur saisie dans notre EmailInput
, nous allons devoir procéder par étapes. Si vous avez du mal à suivre, vous pourrez trouver tout le code nécessaire sur le repo GitHub.
Dans la section juste au-dessus, vous aviez initialisé votre state dans le constructor
avec :
this.state = {
inputValue: '',
}
On va maintenant pouvoir déclarer une fonction pour mettre à jour la valeur de notre state :
updateInputValue = (value) => {
this.setState({ inputValue: value })
}
Pas besoin de const =
devant notre fonction. D'habitude, il aurait fallu l'initialiser, mais ici, étant donné que vous êtes dans une classe
, updateInputValue
est une méthode de votre classe EmailInput
.
On aurait pu mettre updateInputValue
dans le render
, non ?
Eh bien, oui. On aurait pu. Mais comme je vous disais, tout ce qui est dans le render
est exécuté à chaque fois qu'une prop, ou que le state, est mis à jour. Ce qui veut dire que notre fonction serait à nouveau déclarée : pas très performant, n'est-ce pas ?
Et cette méthode est appelée dans onChange
de input
:
<input
onChange={(e) => this.updateInputValue(e.target.value)}
/>
Ici, chaque fois qu'un setState
est effectué, cela va déclencher un nouveau render
de notre composant.
Il existe un certain nombre de règles à respecter pour setState
(le fait que setState
soit asynchrone, que les mises à jour du state soient fusionnées, etc.). Je vous conseille donc de vous renseigner sur ces règles directement dans la documentation de React.
Et de la même manière qu'on a accédé aux props
avec this.props
, c'est this.state
qui nous permet d'accéder au state courant.
Pour afficher le contenu de notre input, on peut donc faire :
render() {
return (
<div>
{this.state.inputValue}
<input
onChange={(e) => this.updateInputValue(e.target.value)}
/>
</div>
)
}
Revenez au this
D'habitude, lorsque nous déclarons une nouvelle fonction, on utilise la syntaxe function myFunction()
. Alors pourquoi ici on a utilisé une fonction fléchée ?
Eh bien, pour pouvoir accéder à votre méthode dans votre classe, vous avez besoin de "binder" votre fonction à votre classe, relier les deux, en quelque sorte. Les fonctions fléchées permettent de le faire de manière implicite. Donc pas d'embêtement ici.
Sans fonction fléchée, autrement, vous auriez dû le faire de manière explicite comme ci-dessous : 👇
constructor(props) {
super(props)
this.updateInputValue = this.updateInputValue.bind(this)
this.state = {
inputValue: '',
}
}
updateInputValue (value) {
this.setState({ inputValue: value })
}
Je sais que le this
peut être un peu complexe à saisir. C'est d'ailleurs une des raisons pour lesquelles React a choisi de favoriser les composants fonction, pour éviter cette complexité au moment de l'apprentissage. Mais n'hésitez pas à creuser un peu le sujet si cela reste trop flou pour vous. Par exemple, cet article de blog (en anglais) revient sur quelques notions fondamentales.
Exercez-vous
C'est le moment de mettre en pratique ce que vous venez de voir. Pour cela, j'ai transformé le composant Card
en composant classe, et créé un composant EmptyList
qui permet de signaler à l'utilisateur que la liste de compétences est vide (s'il a répondu non à toutes les questions). Vous allez devoir :
recréer le système de favori, qui ajoute des "⭐️" autour du nom du freelance sur lequel on clique dans
Card
, en restant sur un composant classe ;"traduire" le composant
EmailInput
en composant fonction ;"traduire" le composant
EmptyList
en composant fonction.
Comme d'habitude, vous trouverez le début de l'exercice sur la brancheP4C1-begin
, et la solution surP4C1-solution
. Alors à vous de jouer ! 🚀
En résumé
Les composants classe sont apparus en même temps que les
class
en JavaScript.Un composant classe est déclaré avec
class NomDuComposant extends Component
.render
est appelé à chaque fois qu’une mise à jour a lieu.Le state est mis à jour avec
setState
, auquel on passe un objet en paramètre.On accède aux props et au state avec
this.props
etthis.state
.
Dans le prochain chapitre, nous verrons un autre aspect essentiel des composants classe : les méthodes de cycle de vie (lifecycle methods), et nous apprendrons à appeler une API dans un composant classe. Alors à tout de suite dans le prochain chapitre ! 🚀