:: Enseignements :: ESIPE :: E3INFO :: 2023-2024 :: Programmation Web avec JavaScript ::
[LOGO]

React


Le but de ce TP est d'apprendre à manipuler les fichiers JSX et React.

Voilà une idée de l'application que l'on veut réaliser

On cherche à écrire une application capable d'afficher tous les films stockés sur le serveur, de supprimer des films (avec le bouton "X") et de pouvoir filtrer l'affichage par les premières lettres du titre des films.

Exercice 1 - ReactDemo

Dans un premier temps, nous avons besoin de deux choses, installer les bibliothèques de React (react et react-dom) et récupérer l'outil esbuild (un exécutable), qui transforme les fichiers .jsx en fichier .js (et bundle les dépendances).
Pour récuperer esbuild, nous allons utiliser npm qui est le gestionnaire de libraries JavaScript et qui permet aussi de récupérer des exécutables (ce qui est stupide en termes de sécurité, mais c'est comme cela ...).
     npm install --save-exact --save-dev esbuild
   

La commande npm ci-dessus installe l'exécutable esbuild dans le répertoire ./node_modules en locale donc pour exécuter esbuild, on utilisera la commande
      ./node_modules/.bin/esbuild --version
    

Puis on va installer, toujours dans ./node_modules, les deux bibliothèques react et react-dom, toujours avec npm
      npm install --save-exact --save-dev react react-dom
    

On peut maintenant écrire un fichier JSX, exo1.jsx puis le transpiler en JavaScript avec esbuild. On utilise pour cela la commande
      ./node_modules/.bin/esbuild exo1.jsx --bundle --outfile=exo1.js
    

Pour démarrer, on va utiliser le fichier exo1.html suivant
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="icon" href="data:,">
    <script src="exo1.js" type="text/javascript"></script>
    <style>
    </style>
  </head>
  <body>
    <div id="App"></div>
    </body>
  </html>
    

Et pour le fichier JSX exo1.jsx
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';

function App() {
  return <>
        <h1>Movie Database</h1>
        <div></div>
    </>;
}

window.onload = () => {
  let appDOM = document.getElementById("App");

  let root = ReactDOM.createRoot(appDOM);
  root.render(<App/>);
};
    

On utilisera de plus le serveur Java JExpress.java pour servir les fichiers exo1.html et exo1.js et l'API REST spécifique aux films.

  1. Utiliser npm pour obtenir esbuild ainsi que les bibliothèques react et react-dom. Recopier les fichiers exo1.html, exo1.jsx et JExpress.java. Utiliser esbuild pour transformer le fichier JSX en JS. Lancer le serveur java JExpress.java dans le répertoire courant et vérifier avec un navigateur que l'application s'affiche bien à l'adresse http://localhost:8080/exo1.html.
    Vous pouvez aussi vérifier le JavaScript généré en ouvrant le fichier exo1.js (le code qui nous intéresse est à la fin du fichier !).
  2. A partir de maintenant, on va modifier le fichier exo1.jsx pour créer notre application, et il ne faudra pas oublier après chaque modification de relancer esbuild ou alors, vous pouvez utiliser l'option --watch.
    On veut maintenant définir un composant MovieList qui dans un premier temps, affiche TODO dans un div.
    Écrire la fonction MovieList et modifier la fonction App pour qu'elle appelle la fonction MovieList.

  3. On souhaite maintenant afficher des films contenus dans un tableau (pour l'instant en dure)
    let data = [
      {"id": "1", "title": "Barbie", "company": "Warner Bros.", "gross": "$1,445,638,421"},
      {"id": "2", "title": "The Super Mario Bros. Movie", "company": "Universal", "gross": "$1,361,992,475"}
    ];
        
    Avec le composant suivant pour chaque item à afficher,
    function MovieItem() {
      return <li>item</li>;
    }
          
    modifier MovieList pour afficher une liste HTML, avec pour l'instant "item" comme nom pour chaque film.
    Rappel: il existe une méthode map() sur les tableaux.
    Pourquoi react affiche-t-il l'erreur "Each child in a list should have a unique "key" prop" sur la console ?

  4. On souhaite maintenant afficher les noms des films et faire disparaitre le warning.
    Modifier MovieItem pour prendre le titre de chaque film en paramètre (props) puis modifier MovieList pour envoyer le titre et ajouter un attribut key.

  5. On cherche à initialiser la liste avec les données du serveur, mais avant de faire cela, on va ajouter l'infrastructure qui permet de mettre à jour un état après le premier affichage en utilisant un state hook
    Comment créer un state hook nommer movies dans le composant MovieList ?
    On va utiliser useEffect() pour exécuter le code qui va mettre à jour movies en utilisant pour l'instant les données en dure du tableau (data).
    Quelle doit être le second paramètre de useEffect sachant que l'on veut appeler le code de l'effet une seule fois ?
    Modifier le code pour utiliser le state hook et initialiser les données dans useEffect(). On peut noter que du point de vue de l'utilisateur, rien n'a changé, l'affichage est identique.

  6. On veut maintenant mettre à jour la liste en appelant le serveur, sachant que l'API REST fourni GET /api/movie qui renvoie un tableau au format JSON de tous les films
    On souhaite écrire la fonction asynchrone fetchMovies qui fait un appel HTTP pour obtenir le tableau des films. Expliquer pourquoi cette fonction doit prendre la fonction de mise à jour des films (setMovies).
    Écrire la fonction fetchMovies et modifier le composant MovieList pour afficher les films à partir des données du serveur.
    Attention à gérer correctement la réponse pour au moins afficher les erreurs sur la console dans fetchMovies.

  7. On souhaite maintenant ajouter un bouton "X" derrière chaque film. Si l'utilisateur clique sur un des boutons, le film correspondant doit être supprimé sur le serveur et il faut ré-afficher la liste.
    Dans un premier temps, on va écrire une fonction asynchrone deleteMovie(id, setMovies) qui supprime le film ayant l'id id en faisant une requète au serveur puis demande le ré-affichage de la liste.
    A quoi sert le second paramètre setMovies ?
    Sachant que sur le serveur, il existe l'API REST
         DELETE /api/movie/:id
        
    avec ":id" l'id du film à supprimer. Écrire la fonction deleteMovie.
    Puis créer une composant MovieDelete qui correspond au bouton avec le texte "X" qui si l'on clique sur le bouton appel deleteMovie.
    Enfin, modifier MovieItem pour intégrer le composant MovieDelete.
    Note: comme on supprime vraiment les films du serveur, si vous voulez obtenir la liste initiale des films, il faut redémarrer le serveur :)

  8. Enfin, on souhaite ajouter un composant textuel permettant de n'afficher que les films qui commencent par les mêmes lettres. Ici, contrairement aux questions précédentes, vous êtes libres de faire comme bon vous semble (mais propre SVP !) sachant qu'il existe l'API REST
         POST /api/movie/:filter
        
    avec ":filter" les premières lettres du filtrage.
    Modifier le code pour ajouter le composant textuel sachant que, par exemple, si l'on supprime un film alors que l'affichage est filtrée, l'affichage reste toujours filtrée.