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

Une application de partage de signets en React - partie 1

Ce TP est le premier d'une série où vous allez créer la base d'une application permettant d'afficher et de gérer une liste de signets.

Afin que les données puissent être partagées entre différents utilisateurs, elles seront centralisées dans une base de données et accédées à l'aide d'une API.

Pour simplifier la conception et la gestion de l'application, les données de l'application seront gérées à l'aide des contextes React et l'affichage des composants sera réalisé à l'aide d'un routeur.

Ce TP, se focalisera sur la mise en place du projet et la réalisation d'une première requête AJAX.

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

  • Utilisation d'une API de données
  • Utilisation des contextes React
  • Utilisation d'un routeur
  • Gestion de données asynchrones

Mise en place du projet

Vous allez créer un nouveau projet React qui se nommera r410-bookmarks. Vous commencerez par créer un sous-répertoire r410-bookmarks dans le répertoire React pour accueillir votre nouveau projet.

Pour faciliter la mise en place du projet, vous recopierez dans ce sous-répertoire les fichiers de base du projet précédent :

  • tous les fichiers à la racine du projet (.gitignore, eslint.config.js, index.html, package.json, README.md et vite.config.js),
  • les fichiers App.jsx et main.jsx du sous-répertoire src,
  • le répértoire public et ses fichiers.

Vous mettrez à jour le projet dans les fichiers package.json et index.html. Vous supprimerez le contenu du fichier index.css. Et vous supprimerez le contenu du composant App.

Ensuite, vous ajouterez la modification nécessaire à la structure de l'application et au CSS pour que la structure de base de votre application soit similaire à la maquette suivante :

Illustration de la structure de base de l'application

Vous fixerez la largeur de l'application, qui restera centrée dans la fenêtre du navigateur si sa largeur est supérieure. Et la hauteur de l'application devra utiliser tout l'espace disponible. La partie centrale devra occuper tout l'espace non requis par l'en-tête et le pied de page.

Puis, vous créerez un dépôt distant associé, nommé lui aussi « r410-bookmarks » auquel vous associerez votre dépôt local. 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
  • Création du projet r410-bookmarks.
  • Structuration de la base de l'application.
  • Création du dépôt GitLab distant pour le TP.

Une première requête

Vous allez commencer par afficher la liste des signets. Dans cette optique, vous allez créer un composant BookmarkList, qui réalisera une requête AJAX vers l'API et affichera, pour l'instant, la liste des noms de signets obtenus.

Dans un premier temps et afin d'organiser votre code, vous centraliserez tout le code relatif à l'API de donnée dans le fichier src/services/api/bookmarks.js. Vous y définirez la constante BASE_URL :

Vous y ajouterez une fonction fetchAllBookmarks() qui utilisera la constante BASE_URL pour réaliser une requête AJAX à l'aide la fonction fetch() vers la ressource « /bookmarks » de l'API. Elle retournera la promesse du contenu du JSON de la réponse de la requête AJAX.

Comme vous l'avez vu au semestre précédent, la fonction fetch() permet de réaliser une requête AJAX et de gérer le résultat asynchrone à l'aide de Promesses. Dans notre cas, la réponse sera de type JSON et le contenu sera obtenu à l'aide de la méthode json() de la réponse.

ESLint devrait vous indiquer une erreur sur l'utilisation de fetch. Le linter vous indique que cette fonction est encore experimentale dans le cadre de Node, mais elle est bien supportée dans les navigateurs. Vous installerez le module JavaScript eslint-plugin-n (npm install eslint-plugin-n --save-dev) et vous désactiverez cette erreur pour ce projet dans la configuration d'ESLint :

Vous pourrez ensuite créer le composant BookmarkList. Ce composant comportera une variable d'état bookmarksData contenant la liste des signets à afficher.

En utilisant un hook d'effet, vous réaliserez une requête vers l'API afin d'obtenir une liste des signets à afficher, que vous stockerez dans la variable d'état bookmarksData, lors de la réponse de l'API.

Dans un premier temps, vous vous contenterez d'afficher le nom de chaque signet, de manière similaire à la maquette suivante :

Illustration de la structure de la liste de signets

Le composant BookmarkList se composera d'un élément HTML de type section comportant une liste d'articles affichant le nom de chaque signet. Vous utiliserez ensuite le composant BookmarkList dans le composant App.

Travail à réaliser
  • Création de la fonction fetchAllBookmarks() dans le fichier « src/services/api/bookmarks.js ».
  • Création du composant BookmarkList.
  • Utilisation du composant BookmarkList dans le composant App.

Le composant d'affichage d'une note

Vous allez avoir besoin d'afficher des notes, vous allez donc créer un composant Rating permettant d'afficher une note.

Le composant Rating recevra une props value contenant la note à afficher. Vous illustrerez la note à l'aide d'une icône étoile de Font Awesome. Et vous pourrez afficher la note en vous inspirant des maquettes suivantes :

Illustration de la structure de base de l'application Illustration de la structure de base de l'application

La props value peut contenir une valeur réelle avec de nombreuses décimales, vous prendrez soin de l'arrondir à la décimale la plus proche avant de l'afficher, à l'aide Math.round par exemple.

Travail à réaliser
  • Arrondi de la note à la décimale la plus proche.
  • Création du composant Rating.

Affichage des signets

Vous créerez ensuite un composant BookmarkItem dont le rôle sera d'afficher un signet dans la liste des signets.

Ce composant recevra une propriété data contenant les données d'un des signets de la liste issue de l'API et affichera sa représentation de manière similaire à la maquette suivante :

Illustration de l'affichage d'un bookmark

Le composant affichera le nom du signet, qui sera le support d'un lien vers l'URL du signet. En plus du nom, la moyenne des notes et l'avatar de l'utilisateur ayant créé le signet seront affichés.

Pour afficher l'avatar d'un utilisateur, vous utiliserez un élément HTML img et pour obtenir l'URL de l'image, vous ajouterez une fonction avatarUrl(userId) au script « bookmarks.js ». Cette fonction recevra en paramètre l'identifiant d'un utilisateur et retournera l'URL de la ressource de l'API fournissant l'avatar de l'utilisateur correspondant.

Vous prendrez soin de définir les prop-types du composant.

Travail à réaliser
  • Création du composant BookmarkItem.
  • Ajout de la fonction avatarUrl(userId).
  • Utilisation du composant BookmarkItem dans le composant BookmarkList.

Extraction de la pagination

Si vous observez la réponse de la liste des signets, vous constaterez que le résultat est paginé et que votre application affiche systématiquement la première page de la requête. Vous allez ajouter le support de la pagination à votre application.

Les données de pagination exportées par l'API ne sont pas très faciles à manipuler. Avant de créer le composant de pagination, vous allez transformer les données de l'API en une structure plus exploitable par votre futur composant.

Dans cette optique, vous ajouterez un module JavaScript « paginationFromCollectionView.js » dans le répertoire « src/services/transformers » de votre projet. Ce script exportera par défaut une fonction qui recevra en paramètre le contenu de la propriété view d'une réponse paginée de l'API et retournera un objet JavaScript comportant les propriétés suivantes :

  • current, une propriété obligatoire contenant la page courante (un nombre), extraite de la propriété @id de la vue.
  • first, une propriété contenant la première page (un nombre), extraite de la propriété first de la vue.
  • previous, une propriété optionnelle contenant la page précédente (un nombre), extraite de la propriété previous de la vue si elle est présente.
  • next, une propriété optionnelle contenant la page suivante (un nombre), extraite de la propriété next de la vue si elle est présente.
  • last, une propriété contenant la dernière page (un nombre), extraite de la propriété last de la vue.

Vous pourrez utiliser le squelette de fichier suivant :

Le paramètre contenant la vue de la réponse paginée de l'API peut être non-défini, si la réponse ne contient qu'une page. Dans ce cas, la fonction devra retourner null.

Pour simplifier l'extraction de la valeur de la page des différentes urls, vous pourrez utiliser la classe URLSearchParams.

Afin de tester la validité de votre fonction, vous afficherez le résultat de son utilisation sur la pagination de la liste de signets obtenue dans le composant BookmarkList.

Travail à réaliser
  • Création de la fonction paginationFromCollectionView dans « paginationFromCollectionView.js ».
  • Utilisation de cette fonction dans le composant BookmarkList.

Mise en place de la pagination

Vous ajouterez une variable d'état au composant BookmarkList stockant la pagination générée à partir de la propriété view de la réponse paginée de l'API.

Vous allez ensuite créer un composant Pagination recevant comme propriété la variable d'état précédente et affichant la page courante ainsi que des boutons vers les première et dernière pages, ainsi que les pages précédente et suivante. Les boutons devront être désactivés lorsque les propriétés ne sont pas présentes dans la pagination.

Vous ajouterez une instance du composant Pagination au composant BookmarkList afin d'afficher l'interface de navigation de la pagination pour la liste des signets.

Travail à réaliser
  • Création du composant Pagination.

Changement de page de la liste des signets

Vous disposez maintenant d'une interface de navigation pour la liste des signets, mais celle-ci n'est pas encore fonctionnelle. Afin de pouvoir faire évoluer la page courante de la liste des signets, vous allez ajouter une variable d'état page au composant BookmarkList avec 1 comme valeur initiale.

Vous réaliserez la mise à jour de cette variable d'état lors du clic sur un des boutons du composant Pagination à l'aide d'une nouvelle props contenant une fonction de rappel, réalisant la mise à jour de la variable d'état page.

Vous pourrez ensuite ajouter un paramètre urlParams de type URLSearchParams à la fonction fetchAllBookmarks dans lequel seront spécifiés les paramètres de la requête AJAX, pour l'instant, il ne contiendra que la page souhaitée.

Pour finir, afin que la requête AJAX soit ré-exécutée lors de chaque modification de la variable d'état page, vous placerez cette variable d'état dans la liste des dépendances du hook d'effet encapsulant la requête.

Travail à réaliser
  • Ajout d'une variable d'état page stockant la page courante.
  • Ajout d'une props au composant Pagination permettant de mettre à jour la variable d'état page.
  • Transmission de la page à la requête AJAX.
  • Mise à jour de la liste des signets lors du changement de page.

Indicateur de chargement

Afin d'améliorer l'expérience utilisateur, il est courant d'utiliser un indicateur de chargement lors du chargement de ressources.

Vous commencerez par créer un nouveau composant Loading.

Dans le composant BookmarkList, vous initialiserez la variable d'état contenant la liste des signets à null avant chaque requête AJAX.

Lors de l'affichage, vous ferez en sorte d'afficher le composant Loading en lieu et place du tableau de composants BookmarkItem lorsque la liste des signets vaut null.

Travail à réaliser
  • Création du composant Loading.
  • Affichage conditionnel du composant Pagination dans BookmarkList.
A. Jonquet DUT-INFO/REIMS