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.
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.
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.
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.
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 :
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.
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.
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.
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.
Card
.
Card
depuis le composant App
.
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 :
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.
title
optionnelle au composant.
title
pour spécialiser les instances de Card
.
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
.
À partir de maintenant, vous utiliserez systématiquement PropTypes pour contrôler tous les composants ayant des propriétés.
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.
children
pour définir le contenu du composant Card
.
Card
.
Card
dans l'application.
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 :
Button
.
Button
dans le composant App
.
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 :
Counter
.
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.
Counter
.
cpt
pour compter les clics des boutons.
cpt
dans le compteur.
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.
onChange
au composant Counter
.
App
.
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 :
contents
un tableau de JSX.Element
non-optionnel et qui devra être typé avec les types arrayOf
, node
et isRequired
,
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 :
CounterList
.
countersContents
.
CounterList
dans App
pour afficher le tableau de contenus.
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.
length
au composant CounterList
.
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.
Counter
.CounterList
.App
.onChange
dans le composant CounterList
.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 :
setValue(idx, value)
permettant d'affecter une valeur du tableau,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
useValues
.useValues
dans le composant CounterList
.App
dans un hook personnalisé useTotal
.