Leçon 4 / 15

Assembler les composants de la page

20 min React — composants et état Gratuit

Objectif : Composer l’écran du tableau de bord avec App et des composants enfants rangés dans des fichiers distincts.

Une page de tableau de bord écrite dans un seul composant devient difficile à parcourir dès que plusieurs zones doivent évoluer séparément. Vous allez organiser l’écran en composants nommés, chacun associé à une partie précise de l’interface. À la fin, App les assemblera sans changer le comportement de la dépense de démonstration déjà transmis à LigneDepense.

Dans la leçon précédente, vous avez créé un composant fonctionnel et transmis une dépense avec des props. Vous réutilisez ici ces acquis : le parent place les enfants dans son JSX, et les props continuent de transporter la dépense jusqu’à LigneDepense.

Attribuer une responsabilité à chaque composant

Un composant est plus simple à maintenir lorsqu’il possède une responsabilité identifiable. Pour cette étape, EnteteDepenses affiche le titre, FormulaireDepense présente la zone de saisie, FiltresDepenses regroupe les filtres, RecapitulatifMensuel affiche le bilan et ListeDepenses contient les lignes. App reste le point d’assemblage de la page.

Définir les composants de présentation

Créez les fichiers ci-dessous dans src/composants. Ces composants posent les différentes zones de l’écran ; ils n’ajoutent pas encore de gestion de saisie, de filtre ou de calcul. Le composant de ligne conserve la prop et le type de dépense définis dans la leçon précédente : gardez exactement l’import réel de Depense utilisé dans votre projet.

src/composants/EnteteDepenses.tsxTexte
export default function EnteteDepenses() {
  return (
    <header className="depenses-entete">
      <h1 className="depenses-entete__titre">Tableau de bord des dépenses</h1>
    </header>
  );
}
src/composants/FormulaireDepense.tsxTexte
export default function FormulaireDepense() {
  return (
    <section className="depenses-saisie" aria-label="Saisir une dépense">
      <h2>Ajouter une dépense</h2>
      <form>
        <label>
          Description
          <input className="depenses-saisie__champ" type="text" name="description" />
        </label>
        <label>
          Montant
          <input className="depenses-saisie__champ" type="number" name="montant" />
        </label>
        <label>
          Catégorie
          <select className="depenses-saisie__champ" name="categorie">
            <option value="">Choisir une catégorie</option>
          </select>
        </label>
        <label>
          Date
          <input className="depenses-saisie__champ" type="date" name="date" />
        </label>
        <button className="depenses-saisie__bouton" type="submit">Ajouter</button>
      </form>
    </section>
  );
}
src/composants/FiltresDepenses.tsxTexte
export default function FiltresDepenses() {
  return (
    <section className="depenses-filtres" aria-label="Filtres des dépenses">
      <label>
        Filtrer par catégorie
        <select className="depenses-filtres__select" name="filtre-categorie">
          <option value="toutes">Toutes les catégories</option>
        </select>
      </label>
      <label>
        Mois
        <input className="depenses-filtres__select" type="month" name="filtre-mois" />
      </label>
    </section>
  );
}
src/composants/RecapitulatifMensuel.tsxTexte
export default function RecapitulatifMensuel() {
  return (
    <section className="depenses-recapitulatif" aria-label="Récapitulatif mensuel">
      <h2>Total du mois</h2>
      <p className="depenses-recapitulatif__montant">0 €</p>
    </section>
  );
}

ListeDepenses contient la zone de liste et affiche le composant LigneDepense. La dépense de démonstration garde les champs et les valeurs déjà employés dans App ; reprenez-les sans les renommer ni les modifier. Importez Depense depuis le même emplacement que dans LigneDepense et App à l’état précédent du projet.

src/composants/ListeDepenses.tsxTexte
import type { Depense } from "../types/Depense";
import LigneDepense from "./LigneDepense";

type ListeDepensesProps = {
  depense: Depense;
};

export default function ListeDepenses({ depense }: ListeDepensesProps) {
  return (
    <section aria-label="Liste des dépenses">
      <h2>Dépenses</h2>
      <ul className="depenses-liste">
        <li className="depenses-liste__element">
          <LigneDepense depense={depense} />
        </li>
      </ul>
    </section>
  );
}

Assembler les zones dans App

Dans src/App.tsx, importez les composants, puis composez la structure dans l’ordre de lecture de la page. L’objet depenseDemo doit reprendre à l’identique la dépense déjà déclarée dans votre App ; l’exemple indique les champs à conserver, mais ne remplacez pas les valeurs définies à la leçon précédente.

src/App.tsxTexte
import EnteteDepenses from "./composants/EnteteDepenses";
import FiltresDepenses from "./composants/FiltresDepenses";
import FormulaireDepense from "./composants/FormulaireDepense";
import ListeDepenses from "./composants/ListeDepenses";
import RecapitulatifMensuel from "./composants/RecapitulatifMensuel";
import type { Depense } from "./types/Depense";

const depenseDemo: Depense = {
  id: "depense-1",
  description: "Courses",
  montant: 42.5,
  categorie: "Alimentation",
  date: "2025-03-12",
};

export default function App() {
  return (
    <div className="depenses-app">
      <EnteteDepenses />
      <main className="depenses-principale">
        <FormulaireDepense />
        <RecapitulatifMensuel />
        <FiltresDepenses />
        <ListeDepenses depense={depenseDemo} />
      </main>
    </div>
  );
}

Le chemin ../types/Depense de cet exemple est à adapter au seul chemin effectivement présent dans votre projet précédent : conservez-le exactement dans tous les fichiers concernés. De même, gardez l’objet de démonstration réellement utilisé jusque-là. Le reste de l’assemblage ne dépend pas de ces détails.

Vérifier le résultat

Lancez le projet : la page affiche le titre, les zones de saisie et de filtres, le total provisoire à zéro et la dépense de démonstration dans la liste. Le navigateur construit toujours la page à partir de App, mais chaque partie visible provient maintenant d’un fichier de composant distinct. Aucun état n’a été ajouté et la dépense continue de descendre de App vers LigneDepense à travers ListeDepenses.

À retenir

  1. App assemble les zones de l’écran et garde une vue d’ensemble de la page.
  2. Chaque composant enfant a une responsabilité limitée et un fichier dédié dans src/composants.
  3. La dépense de démonstration est transmise à la liste, qui la fournit à LigneDepense.
  4. Conservez les classes du contrat et les valeurs réelles déjà définies dans votre projet.

Dans la prochaine leçon, vous ajouterez un état local pour mémoriser la saisie du formulaire.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.