React : construire des interfaces modernes
Quitter

Leçon 1 sur 13 · Les bases de React

Pourquoi React : composants, rendu déclaratif et premier projet avec Vite

40 min Node.js 22 (Debian, git, npm, python3)

Objectif

Comprendre le problème que React résout, comment il met à jour l'écran, où il se place dans l'écosystème (Vite, Next.js, React Router), puis construire à la main la structure d'un projet Vite pour savoir ce que contient chaque fichier.

L'essentiel

  • React est une bibliothèque JavaScript pour construire des interfaces à partir de composants : des fonctions qui renvoient du balisage.
  • Le rendu est déclaratif : vous décrivez l'écran pour un état donné ; React se charge de modifier le DOM quand l'état change.
  • React travaille en deux temps : le rendu (il appelle vos composants pour calculer le résultat) puis le commit (il n'applique au DOM que les différences).
  • React ne fait « que » l'interface. Pour l'outillage, on utilise Vite (serveur de développement, build) ou un framework comme Next.js ou React Router.
  • Un projet Vite tient en quatre pièces : package.json, index.html, src/main.jsx (point d'entrée) et src/App.jsx (premier composant).

Le problème : mettre à jour l'écran à la main

Prenons la coopérative de tapis Tifawin, à Azilal, qui vend en ligne. Dans le cours « Web », vous avez écrit ce genre de code pour afficher le nombre d'articles du panier :

// JavaScript « impératif » : on dit au navigateur QUOI CHANGER, étape par étape
let nombre = 0;
const badge = document.querySelector('#badge-panier');
const bouton = document.querySelector('#ajouter');

bouton.addEventListener('click', () => {
  nombre = nombre + 1;
  badge.textContent = nombre;                 // ne pas oublier de mettre à jour le badge…
  if (nombre > 0) badge.classList.remove('cache');  // …et sa visibilité…
  // …et le total, et le bouton « Commander », et le résumé en bas de page…
});

Tant que l'écran a trois éléments, tout va bien. Avec un catalogue, des filtres, un panier, un formulaire de commande, chaque événement doit penser à toutes les zones à mettre à jour. Les oublis deviennent des bugs : le badge affiche 3, le résumé en affiche 2.

La réponse de React : décrire, pas modifier

Avec React, vous écrivez ce que l'écran doit être pour une valeur donnée. Quand la valeur change, React recalcule et met à jour le DOM pour vous :

import { useState } from 'react';

export default function BoutonPanier() {
  const [nombre, setNombre] = useState(0);   // l'état : la « mémoire » du composant

  return (
    <div>
      <button onClick={() => setNombre(nombre + 1)}>Ajouter au panier</button>
      {nombre > 0 && <span className="badge">{nombre}</span>}
    </div>
  );
}

Il n'y a plus de querySelector ni de textContent : l'interface est une fonction de l'état. On peut le résumer par une formule : interface = f(état).

Les composants : des briques réutilisables

Un composant est une fonction JavaScript dont le nom commence par une majuscule et qui renvoie du balisage (du JSX, vu à la leçon suivante). On les assemble comme des briques :

function CarteProduit({ nom, prix }) {
  return (
    <article>
      <h2>{nom}</h2>
      <p>{prix} DH</p>
    </article>
  );
}

export default function Catalogue() {
  return (
    <main>
      <CarteProduit nom="Tapis Azilal 2 x 3 m" prix={4200} />
      <CarteProduit nom="Coussin en cuir de Fès" prix={350} />
    </main>
  );
}

L'application entière devient un arbre de composants : App contient Entete, Catalogue et Panier ; Catalogue contient des CarteProduit, etc.

Rendu et commit : ce qu'on appelle le « DOM virtuel »

On entend souvent que React utilise un « DOM virtuel ». Concrètement, une mise à jour se déroule en trois étapes :

  1. Déclenchement : au démarrage (rendu initial), ou quand un état change (setNombre(...)).
  2. Rendu : React appelle vos composants. Ils renvoient une description légère de l'interface (des objets JavaScript, pas de vrais nœuds DOM). React compare cette description avec celle du rendu précédent : c'est la réconciliation.
  3. Commit : React applique au vrai DOM uniquement les différences. Si seul le badge a changé, seul le texte du badge est modifié.

Conséquence pratique : un champ de saisie où l'utilisateur tape du texte n'est pas « effacé » par un nouveau rendu, puisque React ne touche pas à ce qui n'a pas changé.

setNombre(3)
   │
   ▼
Rendu  : BoutonPanier() est rappelé → <span>3</span> au lieu de <span>2</span>
   │
   ▼
Commit : un seul changement dans le DOM, le texte du <span>
   │
   ▼
Le navigateur repeint l'écran

Retenez : « rendre » (render) veut dire appeler vos composants, pas forcément toucher au DOM.

L'écosystème : React ne vient pas seul

React s'occupe de l'interface. Une vraie application a aussi besoin de transformer le JSX, de regrouper les fichiers, de gérer les pages, parfois de faire du rendu côté serveur. On choisit donc un outil autour :

Outil Rôle Quand le choisir
Vite serveur de développement très rapide, build de production application « côté client » (SPA) : tableau de bord, back-office, boutique servie en fichiers statiques
Next.js framework complet : pages, rendu serveur, routes d'API site public qui doit être bien référencé, rendu serveur, gros projet
React Router navigation entre pages ; peut aussi servir de framework complet SPA avec plusieurs pages (leçon 2.5)

Dans cette formation, on utilise Vite : c'est le plus simple pour apprendre, et le résultat (des fichiers HTML, CSS et JS) se déploie sur n'importe quel serveur nginx.

React peut aussi être ajouté à une partie d'un site existant (un widget de réservation dans une page PHP, par exemple) : on monte alors React dans un seul élément de la page.

Créer un projet Vite (avec réseau)

Sur votre machine, avec Node.js 20 ou plus récent (voir le cours « Node.js ») :

npm create vite@latest boutique -- --template react   # génère le projet
cd boutique
npm install                    # télécharge react, react-dom, vite…
npm run dev                    # serveur de développement sur http://localhost:5173

Chaque fois que vous enregistrez un fichier, la page se met à jour sans recharger (rechargement à chaud).

Ce que contient le projet

boutique/
├── package.json        # dépendances et scripts
├── index.html          # la seule page HTML : un div vide + le script d'entrée
├── vite.config.js      # configuration de Vite (plugin React)
├── public/             # fichiers copiés tels quels (favicon, robots.txt)
└── src/
    ├── main.jsx        # point d'entrée : monte React dans le div
    ├── App.jsx         # composant racine
    └── index.css

package.json

{
  "name": "boutique-artisanat",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.1.0",
    "react-dom": "^19.1.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^5.0.0",
    "vite": "^7.0.0"
  }
}
  • react contient les composants, le JSX et les hooks ; react-dom sait afficher dans un navigateur.
  • vite et son plugin ne servent qu'au développement et au build : ce sont des devDependencies.

index.html

<!doctype html>
<html lang="fr">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Coopérative Tifawin</title>
  </head>
  <body>
    <div id="root"></div>                                  <!-- React affichera tout ici -->
    <script type="module" src="/src/main.jsx"></script>    <!-- Vite transforme le JSX -->
  </body>
</html>

src/main.jsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
);

createRoot prend le contrôle du div#root ; render y affiche le composant App. StrictMode active des vérifications supplémentaires en développement seulement (par exemple, il appelle deux fois certains codes pour révéler les erreurs).

src/App.jsx

export default function App() {
  return <h1>Coopérative Tifawin</h1>;
}

Préparer son poste

  • Éditeur : VS Code (ou équivalent) avec ESLint et Prettier.
  • Extension React Developer Tools dans Chrome, Firefox ou Edge : elle ajoute les onglets Components et Profiler aux outils de développement (leçon 3.2).
  • Node.js LTS et npm à jour.

Pièges fréquents

  • Nom de composant en minuscule : <carteProduit /> est pris pour une balise HTML inconnue. Un composant commence toujours par une majuscule.
  • Ouvrir index.html directement dans le navigateur (double-clic) : le JSX n'est pas transformé, la page reste blanche. Passez par npm run dev.
  • Mettre vite dans dependencies : ça fonctionne, mais ce n'est pas propre. Les outils de build vont dans devDependencies.
  • Croire que React remplace le JavaScript : tout ce que vous avez appris (tableaux, objets, map, filter, modules, promesses) sert à chaque ligne de React.

Lab

Le lab n'a pas de réseau : vous construisez à la main le squelette exact qu'aurait produit npm create vite, pour la boutique de la coopérative :

  1. le package.json en modules ES ;
  2. les scripts dev, build et preview ;
  3. les dépendances React et Vite (déclarées, non installées) ;
  4. la page index.html ;
  5. le point d'entrée main.jsx et le composant App.jsx.

Quiz

  1. Que signifie « rendu déclaratif » ?
  2. Que fait React pendant la phase de rendu, et pendant le commit ?
  3. Pourquoi vite est-il une dépendance de développement ?
  4. À quoi sert l'élément <div id="root"> d'index.html ?
  5. Quand préférer Next.js à une application Vite ?
Réponses
  1. On décrit l'interface attendue pour un état donné ; React calcule et applique lui-même les changements du DOM.
  2. Rendu : il appelle les composants et compare le résultat au rendu précédent. Commit : il applique au DOM uniquement les différences.
  3. Il ne sert qu'à développer et à construire les fichiers ; le site en production n'en a pas besoin.
  4. C'est le conteneur dans lequel createRoot(...).render(<App />) affiche toute l'application.
  5. Pour un site public qui a besoin de rendu côté serveur (référencement, premier affichage rapide) ou de routes serveur ; Vite convient aux applications côté client servies en fichiers statiques.

Sources de référence : documentation React en français (fr.react.dev, licence CC BY 4.0). Texte original MasterLinux.

Exercices pratiques

À réaliser sur Node.js 22 (Debian, git, npm, python3)

Le terminal en ligne ouvre bientôt. En attendant, réalisez ces tâches sur votre machine Linux, une machine virtuelle ou WSL.

  1. Dans /root/boutique, créez le fichier package.json (npm init -y), puis nommez le projet boutique-artisanat et déclarez-le en modules ES (champ "type" à "module").
    Afficher un indicecd /root/boutique && npm init -y && npm pkg set name=boutique-artisanat type=module
    Voir la solution
    cd /root/boutique && npm init -y >/dev/null 2>&1 && npm pkg set name=boutique-artisanat type=module

    Vérification : node -e "const p = require('/root/boutique/package.json'); process.exit(p.name === 'boutique-artisanat' && p.type === 'module' ? 0 : 1)"

  2. Ajoutez les trois scripts d'un projet Vite : dev qui lance vite, build qui lance vite build et preview qui lance vite preview.
    Afficher un indicenpm pkg set scripts.dev=vite "scripts.build=vite build" "scripts.preview=vite preview"
    Voir la solution
    cd /root/boutique && npm pkg set scripts.dev=vite "scripts.build=vite build" "scripts.preview=vite preview"

    Vérification : node -e "const s = require('/root/boutique/package.json').scripts || {}; process.exit(s.dev === 'vite' && s.build === 'vite build' && s.preview === 'vite preview' ? 0 : 1)"

  3. Le lab n'a pas de réseau : déclarez sans les installer react et react-dom dans les dépendances (dependencies), puis vite et @vitejs/plugin-react dans les dépendances de développement (devDependencies). Chaque version commence par ^ (par exemple ^19.1.0 pour react).
    Afficher un indicenpm pkg set dependencies.react=^19.1.0 dependencies.react-dom=^19.1.0 devDependencies.vite=^7.0.0 "devDependencies.@vitejs/plugin-react=^5.0.0"
    Voir la solution
    cd /root/boutique && npm pkg set dependencies.react=^19.1.0 dependencies.react-dom=^19.1.0 devDependencies.vite=^7.0.0 "devDependencies.@vitejs/plugin-react=^5.0.0"

    Vérification : node -e "const p = require('/root/boutique/package.json'); const d = p.dependencies || {}; const dd = p.devDependencies || {}; const v = (x) => /^\^\d/.test(x || ''); process.exit(v(d.react) && v(d['react-dom']) && v(dd.vite) && v(dd['@vitejs/plugin-react']) && !d.vite ? 0 : 1)"

  4. Écrivez /root/boutique/index.html : une page HTML en français (lang="fr") qui contient un conteneur <div id="root"></div> et charge le module /src/main.jsx avec <script type="module" src="/src/main.jsx"></script>.
    Afficher un indicecat > /root/boutique/index.html <<EOF ... EOF, avec <html lang="fr">, <body>, le div root puis la balise script
    Voir la solution
    printf '%s\n' '<!doctype html>' '<html lang="fr">' '  <head>' '    <meta charset="UTF-8" />' '    <title>Coopérative Tifawin</title>' '  </head>' '  <body>' '    <div id="root"></div>' '    <script type="module" src="/src/main.jsx"></script>' '  </body>' '</html>' > /root/boutique/index.html

    Vérification : f=/root/boutique/index.html; grep -q 'lang="fr"' $f && grep -q '<div id="root"></div>' $f && grep -q '<script type="module" src="/src/main.jsx"></script>' $f

  5. Créez le point d'entrée /root/boutique/src/main.jsx : il importe createRoot depuis "react-dom/client" et le composant App depuis "./App.jsx", puis affiche <App /> dans l'élément d'id root. Créez aussi /root/boutique/src/App.jsx qui exporte par défaut une fonction App renvoyant un titre <h1>.
    Afficher un indiceDeux fichiers : main.jsx (import, createRoot(...).render(<App />)) et App.jsx (export default function App() { return <h1>…</h1>; })
    Voir la solution
    mkdir -p /root/boutique/src && printf '%s\n' 'import { StrictMode } from "react";' 'import { createRoot } from "react-dom/client";' 'import App from "./App.jsx";' '' 'createRoot(document.getElementById("root")).render(' '  <StrictMode>' '    <App />' '  </StrictMode>' ');' > /root/boutique/src/main.jsx && printf '%s\n' 'export default function App() {' '  return <h1>Coopérative Tifawin</h1>;' '}' > /root/boutique/src/App.jsx

    Vérification : m=/root/boutique/src/main.jsx; a=/root/boutique/src/App.jsx; grep -Eq 'import \{ *createRoot *\} from .react-dom/client.' $m && grep -Eq 'import App from .\./App(\.jsx)?.' $m && grep -Eq 'createRoot\(document\.getElementById\(.root.\)\)' $m && grep -Eq '<App */>' $m && grep -Eq 'export default function App\(\)' $a && grep -q '<h1>' $a

Références officielles consultées : Démarrage rapide — https://fr.react.dev/learn ; Installation — https://fr.react.dev/learn/installation ; Ajouter React à un projet existant — https://fr.react.dev/learn/add-react-to-an-existing-project ; Rendu et Commit — https://fr.react.dev/learn/render-and-commit. Texte original MasterLinux.