Il s'agit d'une version statique de l'intranet d'A. Jonquet, certaines fonctionnalités sont donc potentiellement non fonctionnelles.
Rejoindre la version dynamique 🔒
React
Navigation

Introduction au « framework » React

Ce TP est l'occasion de découvrir les concepts de base, les bonnes pratiques, ainsi que les outils associés au développement d'applications en React, largement plébiscités par la communauté.

Le travail consistera à mettre en place un environnement de développement pour React. En plus de l'installation des outils, vous commencerez à manipuler la bibliothèque pour en comprendre les concepts de base.

Remarque importante

Les notes des TP seront obtenues à partir de vos dépôts Git, vous devrez donc prendre grand soin de la qualité de ces dépôts et de leurs « commits ».

De manière similaire, les descriptions de vos « commits » devront être claires et informatives, afin de permettre l'évaluation de la progression de votre travail.

Objectifs de la séance

  • Mise en place d'un environnement de travail React
  • Introduction aux concepts de React
  • Introduction à la création de composants

Introduction à React

La communauté React fait un vrai effort de pédagogie, et le site de React propose une documentation bien faite sur les différents concepts de la bibliothèque. Notamment, la section « Apprenez React » propose une présentation très progressive des concepts de React. Vous allez mettre en pratique ces concepts dans la suite du TP, mais vous pourrez toujours y revenir pour assoir votre compréhension des concepts de la bibliothèque. Vous trouverez aussi une documentation technique de la bibliothèque.

Travail à réaliser

Mise en place d'un projet React

Remarque importante

Le système de fichier en réseau du département informatique étant un peu poussif sur les accès aux fichiers, et les projets React sont constitués de nombreux fichiers. Vous êtes encouragé à travailler dans le répértoire « /working » de votre machine. Ce répertoire est un répertoire local à la machine, il n'utilise donc pas le système de fichier du département. En contrepartie, votre projet ne sera pas sauvegardé sur les serveurs du département et ne sera accessible que de cette machine. Vous penserez impérativement à sauvegarder votre travail sur le serveur GitLab au moins à chaque fin de séance.

De plus, toujours pour ne pas solliciter le système de fichier du département, vous devez déplacer le cache du gestionnaire de paquets de Node afin qu'il ne soit plus sur votre compte, mais dans le répertoire temporaire de la machine.

Remarque importante

Les éditeurs peuvent de temps en temps afficher une erreur sur l'import d'une nouvelle dépendance, comme par exemple prop-types même lorsque celui-ci était défini dans les dépendances du projet.

Un simple redémarrage de l'éditeur permet généralement de régler le problème.

Vous commencerez par créer un nouveau répertoire de travail pour l'ensemble du module que vous pourrez appeler React.

Il est tout à fait possible de mettre en place un projet React « à la main », mais l'installation et la configuration des différents paquets requis se révèlent vite fastidieuses. Pour pallier cela, la communauté React a mis au point des outils permettant d'installer un ensemble d'outils de base et de les configurer afin de répondre aux bonnes pratiques du développement d'application en React. Nous allons utiliser vite pour mettre en place le projet.

Vous allez initialiser votre projet à l'aide de la commande suivante :

npm init vite r410-introduction

Cette commande permet d'initialiser un projet npm, puis d'exécuter la commande vite permettant de définir les dépendances du projet en fonction du type de projet. Dans notre cas :

  • framework : React,
  • variant : JavaScript.

Puis comme indiqué, vous pouvez ensuite vous rendre dans le répertoire du projet et installer toutes les dépendances :

cd r410-introduction
npm install

Cet outil permet de créer une structure de projet React fonctionnelle. Vous pourrez détailler le contenu des fichiers et répertoires créés avec votre intervenant de TP ou encore avec la documentation.

Vous contrôlerez que tout fonctionne correctement en lançant le serveur de développement npm run dev. Vous pouvez consulter le fichier package.json pour une liste des commandes.

Dans le répertoire src, vous supprimerez l'ensemble des fichiers à l'exception des fichiers « App.jsx », « main.jsx » et « index.css ». Vous remplacerez ensuite le contenu des fichiers « App.jsx » et « index.css » en y copiant les contenus suivants : App.jsx (télécharger) et index.css (télécharger)

Vous devriez obtenir une application à nouveau fonctionnelle dans votre navigateur.

Vous pourrez modifier un peu le contenu du composant App et constater le rechargement « à chaud » de l'application.

Vous finaliserez la mise en place du projet en créant un dépôt Git local dans le répertoire r410-introduction. Vous créerez alors un dépôt distant associé, nommé r410-introduction, puis vous réaliserez votre premier commit.

git init --initial-branch=main
git remote add origin https://iut-info.univ-reims.fr/gitlab/VOTRE_LOGIN/r410-introduction.git
git add .
git commit -m "Initial commit"
git push -u origin main

Vous penserez à ajouter votre intervenant de TP comme membre du dépôt distant, avec un rôle au moins de Reporter.

Travail à réaliser
  • Mise en place de la structure du module.
  • Création du projet React à l'aide de l'outil vite.
  • Nettoyage du projet.
  • Mise en place du versionnage du projet et de la sauvegarde (r410-introduction).

Qualité de code : ESLint et Prettier

Vite a déjà installé et configuré ESLint comme « linter » du projet. Mais les règles par défaut sont assez permissives et succinctes pour le style du code. Vous allez donc ajouter des règles supplémentaires pour renforcer la qualité du code.

Afin de ne pas configurer vous-même toutes les règles, vous allez utiliser comme base la configuration ESLint de l'équipe de développement d'ESLint. Vous l'installerez à l'aide de la commande suivante :

npm install eslint-config-eslint --save-dev

Pour ajouter le support de Prettier, vous installerez le module Prettier ainsi que le module permettant d'exécuter Prettier comme un ensemble de règles d'ESLint et celui désactivant les règles d'ESLint en conflit avec Prettier :

npm install --save-dev eslint-config-prettier eslint-plugin-prettier prettier

Vous modifierez le fichier de configuration d'ESLint pour qu'il utilise ces modules en lui ajoutant les lignes suivantes :

Vous devriez constater dans votre éditeur plusieurs erreurs sur votre projet. Ces erreurs ne sont pas nécessairement graves, mais elles ont vocation à imposer des règles permettant de renforcer la cohérence et l'uniformité du code.

Vous devez faire en sorte de résoudre ces erreurs.

Vite a défini une commande npm permettant de controller les erreurs du projet : npm run lint. Vous pouvez transmettre des options supplémentaires à la commande lint en utilisant -- pour qu'ESLint essaye de corriger les erreurs automatiquement :

npm run lint -- --fix

Vous pourrez ajouter une commande npm pour fixer votre projet à l'aide de la commande précédente.

ESLint devrait avoir résolu toutes vos erreurs à l'exception de la documentation du composant App, qui requiert votre intervention. Ici, ESLint vous demande de documenter la fonction exportée au format JSDoc. Vous pourrez vous inspirer du commentaire suivant pour résoudre les erreurs :

Nous avons choisi un jeu de règles par souci de simplicité, mais si une règle ne vous convient pas, vous pouvez et devez modifier la configuration d'ESLint pour le refléter.

Dans la suite des TP, vous prendrez soin de résoudre tous les problèmes d'ESLint et de Prettier.

Travail à réaliser
  • Mise en place d'un jeu de règles pour ESLint.
  • Mise en place de Prettier.
  • Configuration d'ESLint.
  • Correction des erreurs dans le code.

Gestion du CSS et des « assets »

Il existe de nombreuses façons de gérer le style des composants de React. Par défaut la commande vite encourage l'import de CSS dans le JavaScript. Cette pratique permet de décomposer le CSS avec une vision par composant. Cependant, cette simplification est obtenue au prix d'une complexification de la structure du projet.

Afin de simplifier l'introduction aux concepts de React et de nous ramener à une approche plus classique, vous placerez tous les fichiers non compilés, les « assets » (image, video, son, …), dans le répertoire public. Et vous utiliserez le fichier CSS index.css comme unique source CSS.

Vous déplacerez donc le fichier CSS index.css dans le répertoire public en ajoutant son utilisation dans le fichier index.html du projet et vous supprimerez son import dans le fichier main.jsx.

Travail à réaliser
  • Déplacement du fichier CSS index.css de src à public.
  • Ajout de l'utilisation de index.css dans index.html.

Un premier composant

React est une bibliothèque reposant sur les paradigmes du développement orienté composant, vous allez donc ajouter un premier composant à votre application.

Vous ajouterez le fichier Card.jsx (télécharger) suivant dans un sous-répertoire components du répertoire src :

Il s'agit de l'implémentation la plus simple d'un composant que l'on puisse réaliser. Vous pourrez noter l'usage de JSX pour simplifier l'écriture du composant.

Ce type de composant est appelé composant-fonction, il existe un autre type de composant reposant sur l'utilisation de classe, dit composant-classe, mais ils ont l'inconvénient d'être plus verbeux dans leur écriture. Ces composants-classes ont longtemps été nécessaires pour la gestion de données par les composants, cependant, depuis sa version 16.8, React propose une alternative pour les composants-fonctions. Dans le cadre de ce cours, nous n'utiliserons que des composants-fonctions.

Pour commencer, en vous inspirant de l'import et de l'utilisation du composant App dans le fichier main.jsx, vous importerez le composant Card dans le module App.jsx et vous ajouterez ensuite 3 instances de ce composant dans le rendu de l'application grâce à la syntaxe JSX.

rendu des 3 instances de Card
Travail à réaliser
  • Ajout d'un composant Card.
  • Utilisation du composant Card depuis le composant App.

Les « props »

Nous avons maintenant 3 cartes affichées dans l'application, parfaitement alignées et identiques. Même s'il est intéressant de pouvoir regrouper une structure HTML dans un composant, il serait beaucoup plus intéressant de pouvoir la personnaliser pour chaque instance du composant.

Si l'on souhaite obtenir des cartes avec différents titres, le composant doit être dupliqué pour chacun des titres, ce qui révèle une conception malheureuse. En réalisant une analogie avec des fonctions, nous identifions rapidement comment factoriser le code. Pour l'instant, nous avons des composants similaires à ces fonctions, très spécialisées :

La solution consiste à rendre générique la fonction en acceptant des paramètres, correspondant aux valeurs à additionner. De cette manière, toutes les fonctions d'addition spécialisées peuvent être remplacées par une seule fonction plus générique :

Sur le même principe, il est possible de passer des paramètres aux composants de React, qui seront appelés des « props ».

En JSX, la transmission d'une props à un composant se fait en utilisant la syntaxe des attributs HTML. Par exemple, dans le cadre suivant, le nombre 2 sera associé à l'attribut a et le nombre 10 sera associé à l'attribut b des props qui seront transmises au composant Addition :

Dans le composant React, l'ensemble des props transmis à un composant sera regroupé dans un objet JavaScript et passé en paramètre du composant-fonction concerné. Avec le code précédent, lors de l'instanciation du composant le paramètre props aurait la valeur suivante :

Il est courant, comme illustré dans la documentation, d'utiliser l'affectation par décomposition (« destructuring assignment »), qui permet de créer à la volée des variables correspondantes aux propriétés souhaitées. Cette syntaxe, en plus d'alléger le code, permet simplement de fournir des valeurs par défaut aux propriétés pour les rendre optionnelles. Le réusinage du code précédent donnerait le résultat suivant :

Dans un premier temps, nous allons nous intéresser au titre des cartes. Vous commencerez par ajouter une props title à votre composant Card contenant le texte du titre de la carte. Vous rendrez cette props optionnelle en lui affectant une valeur nulle par défaut.

Vous noterez l'usage des accolades au sein du JSX pour afficher le contenu d'une variable JavaScript. Il s'agit d'une manière pratique d'intégrer des expressions JavaScript dans le JSX.

Vous devriez obtenir 3 cartes sans titre dans votre application puisqu'aucune valeur n'a été affectée au paramètre title lors de la création des composants Card dans le composant App. Cependant, dans la page, l'espace du titre est réservé même si celui-ci n'a pas de valeur. Pour résoudre ce problème, vous rendrez l'affichage de l'élément HTML header du composant Card conditionnel à la valeur de la props title. Plusieurs solutions sont envisageables, la plus courante et la plus concise est probablement l'utilisation de l'opérateur paresseux « && ».

Après vos modifications du composant Card, vous modifierez les instanciations de composants Card du composant App afin d'obtenir un rendu similaire à celui-ci :

nouveau rendu des 3 instances de Card
Remarque importante

ESLint devrait vous indiquer une erreur sur l'utilisation de la props title dans votre composant. En effet, les bonnes pratiques voudraient que vous contrôliez la présence et le type de cette props. Vous pouvez ignorer cette erreur pour l'instant, vous allez la résoudre dans les prochaines sections.

Travail à réaliser
  • Ajout d'une props title optionnelle au composant.
  • Rendre optionnel l'affichage du titre de la carte.
  • Utilisation de cette props title pour spécialiser les instances de Card.

Contrôle des props

Avant de passer à la suite, revenons sur l'erreur ESLint de la props title du composant Card. JavaScript est un langage interprété, c'est-à-dire que le langage est directement lu et exécuté par le navigateur, sans avoir à passer par un langage compilé intermédiaire. Le langage ne dispose donc pas de moyen de contrôle du code avant son exécution pour détecter, par exemple, qu'un paramètre n'est par valide.

Dans notre cas, si une valeur, qui ne peut pas être affichée, est transmise comme titre d'une carte, votre application s'arrête et les messages d'erreurs ne sont pas très explicites. Vous pouvez faire l'expérience en transmettant un objet JavaScript comme valeur de titre (title={{}}).

Afin d'améliorer la gestion des props et de rendre l'utilisation d'un composant plus robuste, il est recommandé d'utiliser des outils de contrôle des props comme le module PropTypes. Celui-ci vous permettra de lister les propriétés d'un composant, d'en assurer une description précise. Dans le cas présent, vous définirez la props title comme une chaîne de caractères.

L'installation du module eslint-plugin-react a déjà installé le module PropTypes, vous pouvez donc directement l'utiliser dans votre projet. La déclaration des PropTypes pour le composant Card devrait ressembler au code suivant :

Il est à noter que les PropTypes n'empêchent pas les erreurs qui stoppent l'application, mais réalisent seulement des avertissements dans la console de développement du navigateur. En transmettant une valeur invalide comme titre d'une carte, vous devriez voir un message d'avertissement dans la console de développement vous indiquant qu'une valeur invalide a été transmise à un composant Card dans le composant App.

Remarque importante

À partir de maintenant, vous utiliserez systématiquement PropTypes pour contrôler tous les composants ayant des propriétés.

Travail à réaliser
  • Ajout de contraintes sur les props à l'aide de PropTypes.

Délégation de contenu

Afin de rendre les composants les plus génériques possible, les bonnes pratiques React encouragent la délégation de contenu. Il s'agit de permettre à des composants de recevoir comme props un contenu à afficher au sein du composant. Cette démarche permet de déléguer à l'utilisateur du composant le contenu d'une ou plusieurs parties de son contenu.

Dans notre cas, le composant Card que vous venez de créer pourrait afficher un paragraphe de texte avec une image, un carousel d'images ou une vidéo. Si l'affichage du contenu de la carte est géré directement dans le composant, alors il faudra faire un affichage spécifique en fonction du type de paramètre passé : un tableau d'image, une vidéo, du texte… Et si un nouveau type d'affichage doit être ajouté, alors le code du composant doit être mis à jour.

Une solution plus élégante consiste à recevoir directement le contenu à afficher sous forme d'une props et de l'ajouter tel quel au rendu JSX du composant, rejetant alors la responsabilité du contenu sur l'utilisateur du composant.

Ici le code du composant Card ne connait pas la nature du contenu qu'il doit afficher, sa responsabilité concerne uniquement l'affichage de la carte.

Cette pratique illustre le fait qu'il est tout à fait possible de fournir du JSX à une propriété d'un composant qu'il pourra alors insérer dans son propre JSX.

Dans le cas où il n'y a qu'un contenu à transmettre au composant, il est généralement plus naturel d'utiliser la propriété spéciale children pour transmettre le contenu du composant. Le contenu du composant est alors défini par ce qui est encadré entre les balises ouvrante et fermante du composant.

Vous modifierez le composant Card pour initialiser le contenu de la carte à l'aide de la props children, qui sera nulle par défaut.

Pour résoudre les soucis d'ESLint qui devraient apparaître dans le composant Card, vous ajouterez la documentation JSDoc de la props children avec un type JSX.Element et vous typerez la props children avec le type node.

Vous modifierez les composants Card du composant App afin d'obtenir un rendu similaire à celui-ci :

Le contenu de la première carte est obtenu à l'aide du contenu des exemples précédents.

Travail à réaliser
  • Utiliser une props children pour définir le contenu du composant Card.
  • Mise à jour des PropTypes du composant Card.
  • Modifier en conséquence les appels au composant Card dans l'application.

Un second composant

Vous allez maintenant créer un nouveau composant Counter. Il s'agira d'un composant relativement simple, permettant de compter le nombre de fois qu'il est cliqué. Pour cette section, nous ne nous intéresserons qu'à l'affichage.

Vous allez créer un composant Counter qui recevra une props children, de type node et null par défaut.

Ce composant retournera un élément HTML button contenant comme classe CSS btn. Le bouton contiendra entre ces balises le contenu de la props children suivi d'un nombre, 0 pour l'instant.

Vous ajouterez deux instances de ce composant à la seconde carte de votre application, pour obtenir un rendu similaire à celui-ci :

Travail à réaliser
  • Création du composant Button.
  • Utilisation du composant Button dans le composant App.

Utilisation de polices d'icônes (icon fonts)

Vous allez mettre en place des compteurs de clics. Et afin de leur donner un peu de personnalité, vous allez leur ajouter une petite icône.

Les polices d'icônes sont devenues courantes dans le monde du web. Dans le cadre de React, elles sont généralement encapsulées dans des bibliothèques de composants.

Vous allez utiliser les icônes Font Awesome, mais pour y avoir accès, vous allez commencer par installer les paquets npm :

npm install @fortawesome/fontawesome-svg-core \
            @fortawesome/free-solid-svg-icons \
            @fortawesome/react-fontawesome

Une fois les paquets installés, vous pourrez afficher une icône dans chaque bouton, comme heart ou star, par exemple, ou n'importe quel autre icône pour obtenir un rendu similaire à celui-ci :

Travail à réaliser
  • Installation des icônes Font Awesome.
  • Utilisation des icônes Font Awesome dans le composant Counter.

Ajout d'interaction

Pour l'instant, le composant Counter n'est pas interactif. Vous allez ajouter un écouteur d'événements sur le clic de la souris pour pouvoir y remédier.

Comme précisé dans la documentation, l'ajout d'un écouteur d'événements en React est très similaire à la syntaxe HTML. Dans un premier temps, dans le composant Counter, vous attacherez une fonction de rappel réalisant l'affichage d'un message dans la console de développement lors du clic de la souris.

Vous ajouterez ensuite une variable, initialisée à 0, pour compter les clics que vous afficherez dans le contenu du bouton (précédemment 0). Et vous incrémenterez cette variable à chaque clic de la souris avant d'afficher sa valeur dans la console de développement.

Vous devriez constater que la valeur de la variable s'incrémente à chaque clic de la souris, mais que la valeur affichée dans le bouton, sur la page, ne change pas. C'est parce que React ne rafraîchit pas l'affichage du composant lors des modifications des variables JavaScript. Pour remédier à ce problème, il faut introduire la notion d'état de composant. En effet, pour qu'un composant soit conscient des modifications d'une variable, celle-ci doit faire partie de son état.

Historiquement, pour utiliser un état dans un composant React, il était nécessaire d'avoir recours aux « composants-classes ». Mais depuis l'introduction des hooks, il est possible d'ajouter des variables d'état aux « composants-fonctions ». Il s'agit de la solution que nous privilégions dans les TP.

L'utilisation du « hook d'état » permet de définir des variables spéciales, qui sont associées à l'état du composant dans lequel ils sont définis. Lorsque ces variables seront modifiées, React provoquera le rafraîchissement de l'affichage du composant associé.

L'appel à useState retourne un tableau avec la variable dans la première case et la fonction de modification dans la seconde. Vous pouvez initialiser la valeur de la variable en passant une valeur en paramètre de la fonction useState.

Vous devez impérativement utiliser la fonction de modification retournée par useState pour modifier la valeur de la variable, car c'est l'appel à cette fonction qui informe React qu'il doit mettre à jour le composant.

Sur ce principe, vous ajouterez une variable d'état à votre composant, pour gérer la valeur du compteur. Et vous incrémenterez sa valeur dans la fonction de rappel attachée au clic de la souris.

Vous devriez constater que maintenant la valeur affichée dans les boutons augmente lorsque ceux-ci sont cliqués.

Travail à réaliser
  • Gestion du clic de la souris dans le composant Counter.
  • Test de l'utilisation d'une variable cpt pour compter les clics des boutons.
  • Ajout d'un état cpt dans le compteur.
  • Modification de cet état lors du clic de la souris.

Remontée d'information au parent

Vous disposez maintenant de deux compteurs indépendants. Chaque compteur est autonome, car son état et sa logique sont internalisés dans le composant, ce qui rend son utilisation plus simple, il suffit de l'ajouter à l'application et il est fonctionnel.

Cependant, il n'est pas possible d'afficher le total des valeurs des compteurs, car nous n'avons pas accès à la valeur des compteurs depuis le composant App. Pour cela, il est nécessaire de remonter l'information depuis le composant Counter vers le composant App.

Pour faire remonter une information d'un composant vers son parent, vous devez utiliser une fonction de rappel, qui sera fournie par le parent lors de l'instanciation du composant et qui sera invoquée par le composant en transmettant en paramètre les données à faire remonter.

Vous ajouterez donc, au composant Counter, une props onChange de type fonction, valant null par défaut. Cette props sera invoquée, si elle n'est pas null, en lui fournissant la valeur du compteur comme paramètre, à chaque fois que la valeur du compteur sera modifiée, lors du clic de la souris.

Dans un premier temps, dans le composant App, vous associerez une fonction de rappel à la props onChange des compteurs affichant la valeur du compteur reçu en paramètre.

Ensuite, vous ajouterez une variable d'état, initialisée à 0, stockant le total des compteurs et vous afficherez cette valeur dans la dernière carte du composant App. Pour l'instant, nous n'avons pas encore besoin de la valeur du compteur, l'information qu'ils sont cliqués est suffisante. Lors du clic d'un compteur, vous incrémenterez la valeur du total des compteurs.

Vous devriez maintenant voir le total se mettre à jour automatiquement lors du clic sur les compteurs.

Travail à réaliser
  • Ajout d'une props onChange au composant Counter.
  • Ajout et affichage d'une variable d'état pour le total des compteurs dans le composant App.
  • Modification de la variable d'état du total des compteurs lors de la modification des compteurs.

Gestion des tableaux

Il est courant de disposer d'un tableau de données et de souhaiter en produire un tableau de composants à afficher. React propose l'affichage direct des composants du tableau lorsque le tableau JavaScript le contenant est inséré dans du JSX. Il est donc courant de voir recourir à la méthode map pour convertir un tableau de données en un tableau de composants qui est ensuite injecté dans du JSX pour l'afficher. Cependant, pour gérer le rafraîchissement des composants, React requiert que les différents composants du tableau soient identifiés par une propriété unique : key.

Dans notre cas, nous souhaitons pouvoir afficher une liste de compteurs depuis un tableau des contenus JSX des compteurs. Vous ajouterez le fichier « countersContents.jsx » (télécharger) suivant dans un sous-répertoire « data » du répertoire src :

Ce fichier exporte un tableau de contenus JSX pour les compteurs, permettant de générer une liste de compteurs à afficher.

Vous créerez ensuite un nouveau composant CounterList qui recevra comme props :

Ce composant retournera un élément HTML div avec la classe CSS btns qui contiendra la liste de compteurs générée à partir du tableau contents reçu en props.

Vous ajouterez ce tableau au rendu du composant App, dans la seconde carte en remplacement des compteurs précédents, en lui transmettant le tableau de contenu exporté par le module countersContents.jsx. Vous devriez obtenir un rendu similaire à celui-ci :

Travail à réaliser
  • Création du composant CounterList.
  • Ajout du tableau de contenus countersContents.
  • Utilisation du composant CounterList dans App pour afficher le tableau de contenus.

Vie d'un composant

Avant de poursuivre, vous allez vous occuper du sélecteur de nombre de compteurs que vous avez dans votre première carte et que nous avons laissé de côté pour le moment.

Conformément à la documentation, vous allez en faire un champ de saisi contrôlé en ajoutant un nouvel état au composant App, qui contiendra le nombre de compteurs à afficher. Et vous associerez cet état au slider afin que l'utilisateur puisse choisir une valeur entre 0 et la taille du tableau de contenus. Vous afficherez la valeur pour contrôler le bon fonctionnement de votre code.

Maintenant, vous ajouterez une nouvelle props length au composant CounterList de type numérique permettant de sélectionner le nombre de compteurs à afficher.

Pour limiter la taille du tableau de compteurs, vous pouvez utiliser la méthode slice, bien que non optimale, elle a l'avantage de simplifier votre code.

Vous devriez maintenant être capable de faire varier le nombre de compteurs à afficher en faisant varier le slider. Cependant, si vous utilisez un peu les compteurs, puis, que vous les cachez pour ensuite les afficher à nouveau, vous constaterez que leur valeur a été remise à 0.

C'est tout à fait logique, lorsque vous avez masqué les compteurs, vous les avez supprimés du JSX, ils ont donc été réellement supprimés et ils s'agit donc de nouvelles instances lorsque les compteurs sont affichés à nouveau.

Les variables d'état d'un composant sont associées à la vie du composant, elles sont créées lors de la création du composant et détruites lors de sa suppression. Et la vie du composant est associée à sa présence dans le DOM. Lorsqu'un composant est retiré du DOM, il est détruit et lorsqu'il est réinséré, il est recréé.

Pour conserver les valeurs des compteurs, vous allez devoir stocker ces valeurs dans le parent afin qu'elles persistent à la suppression des compteurs. Ce tableau de données nous servira aussi à conserver un total qui reste valide.

Mais avant de régler ces soucis, vous avez dû constater un message d'avertissement dans la console de développement lors de l'utilisation du slider. En effet, vous avez dû définir le type de la props length comme numérique, mais le résultat de la propriété value de l'élément HTML représentant le slider est une chaîne de caractères. Vous utiliserez la fonction parseInt pour convertir la chaîne de caractères en entier.

Travail à réaliser
  • Gestion du slider pour sélectionner le nombre de compteurs à afficher.
  • Ajout d'une props length au composant CounterList.
  • Constatation de la durée de vie des composants.
  • Conversion de la valeur du slider.

Persistance des valeurs des compteurs

Comme nous venons de le voir, pour faire persister les données des compteurs, vous allez devoir stocker ces données dans le parent des compteurs et cette valeur sera transmise aux compteurs lors de leur création.

Vous commencerez par ajouter une nouvelle props d'état defaultValue au composant Counter pour initialiser la variable d'état du compteur. Cette variable sera égal à 0 par défaut.

Vous ajouterez ensuite une nouvelle variable d'état au composant CounterList, qui contiendra le tableau des valeurs des compteurs. Cette variable sera initialisée avec un tableau de 0, vous pourrez utiliser la methode fill pour remplir un tableau initialisé à la même taille que le tableau de contenus.

Vous utiliserez cette variable d'état pour initialiser les compteurs, lors de leur création.

Vous créerez une fonction setValue qui recevra en paramètre l'indice de la case du tableau à mettre à jour et la valeur à y affecter. Pour la mise à jour de la variable d'état contenant le tableau des valeurs des compteurs, vous prendrez soin de ne pas modifier directement le tableau, mais de créer un nouveau tableau avec les valeurs mises à jour. Conformément à la documentation de React, vous pourrez utiliser la méthode map pour cela.

Dans un premier temps, vous remplacerez la fonction de rappel onChange des compteurs par l'invocation de cette fonction dans une fonction anonyme. Cette fonction anonyme recevra en paramètre la nouvelle valeur à affecter et, étant générée lors du parcours des contenus, elle disposera du numéro d'indice du compteur concerné. Le total des compteurs ne devrait plus être mis à jour, mais les compteurs devraient maintenant conserver leur valeur pendant l'utilisation du slider.

Pour mettre à jour le total des compteurs, le composant App doit avoir accès au tableau de valeurs des compteurs. Comme précédemment, pour faire remonter une information d'un composant (CounterList) vers le parent (App), vous allez utiliser une fonction de rappel. Vous modifierez la fonction de rappel fournie à la props onChange de CounterList dans App pour qu'elle reçoive en paramètre un tableau des valeurs de compteurs affichés, qu'elle en calcule le total qu'elle affectera à la variable d'état correspondante.

Pour finir, dans le composant CounterList, vous devez invoquer la props onChange pour chaque modification du tableau des valeurs, mais aussi lors du changement du nombre de compteurs affichés. Une solution pratique consiste à utiliser le hook useEffect. En effet, ce hook permet de provoquer un effet de bord, modélisé dans le code de la fonction de rappel passé en premier paramètre, lorsqu'une des variables du tableau de dépendances, passé en second paramètre, change de valeur.

Vous utiliserez donc le hook useEffect pour invoquer la props onChange lors des modifications du tableau des valeurs ou du nombre de compteurs affichés.

Vous devriez maintenant avoir retrouvé une application fonctionnelle.

Travail à réaliser
  • Ajout de l'initialisation du composant Counter.
  • Ajout d'une variable d'état contenant la liste des valeurs de compteurs dans le composant CounterList.
  • Calcul de la somme des valeurs dans la fonction de rappel dans le composant App.
  • Utilisation du hook d'effet pour invoquer la props onChange dans le composant CounterList.

Ménage et hooks personnalisés

Votre application est fonctionnelle, mais le code de votre composant CounterList commence à être un peu long et complexe. Vous allez donc extraire une partie de la logique de ce composant dans un hook personnalisé. Les hooks personnalisés sont des fonctions qui encapsulent de la logique réutilisable et qui peuvent être utilisées dans plusieurs composants.

Vous ajouterez un module JavaScript « useValues.js » dans le sous-répertoire data du répertoire src. Ce module exportera par défaut une fonction :

  • recevant en paramètre la taille du tableau de donnée à gérer,
  • créera une variable d'état contenant un tableau de la taille passée en paramètre remplie de 0,
  • créera une fonction setValue(idx, value) permettant d'affecter une valeur du tableau,
  • et retournera un object JavaScript contenant deux propriétés ; le tableau de valeurs et la méthode setValue

Vous utiliserez ce hook dans le composant CounterList pour gérer le tableau des valeurs des compteurs. La logique du composant étant externalisée, le composant devrait maintenant être un peu plus lisible.

Sur le même principe, vous extrairez la gestion du total du composant App dans un hook personnalisé useTotal

Travail à réaliser
  • Création d'un hook personnalisé useValues.
  • Utilisation du hook personnalisé useValues dans le composant CounterList.
  • Extraction de la gestion du total du composant App dans un hook personnalisé useTotal.
A. Jonquet DUT-INFO/REIMS