Aller au contenu

Les composants dans React

Introduction

React repose sur une idée très simple : un composant est une fonction qui retourne du JSX. En construisant votre application comme un assemblage de composants, vous obtenez :

  • Modularité : chaque composant a une responsabilité claire.
  • Réutilisabilité : un même composant peut être utilisé à plusieurs endroits.
  • Maintenabilité : une modification locale n’impacte qu’une partie du code.

Un premier composant

Définition

HelloWorld.jsx
function HelloWorld() {
return <h1>Hello, World!</h1>;
}
export default HelloWorld;

Un composant fonctionnel est simplement une fonction JavaScript qui retourne un élément JSX.

Utilisation

App.jsx
import HelloWorld from './HelloWorld';
function App() {
return (
<div>
<HelloWorld />
</div>
);
}
export default App;

Le composant est importé comme n’importe quel module, puis utilisé comme une balise JSX. C’est ce mécanisme de composition — des composants dans des composants — qui permet de construire des interfaces complexes à partir de petites briques.

Composer des composants

Une page web se décompose naturellement en sections. Dans React, chaque section devient un composant.

Exemple : une page de profil

PageProfile.jsx
import Header from './Header';
import ProfileInfo from './ProfileInfo';
import Posts from './Posts';
import Footer from './Footer';
function PageProfile() {
return (
<div>
<Header />
<ProfileInfo />
<Posts />
<Footer />
</div>
);
}
export default PageProfile;

L’arbre des composants ressemble à ceci :

App
└── PageProfile
├── Header
├── ProfileInfo
├── Posts
└── Footer

Point d’entrée de l’application

main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import PageProfile from './PageProfile';
createRoot(document.getElementById("root")).render(
<StrictMode>
<PageProfile />
</StrictMode>
);

Retourner plusieurs éléments

Un composant doit toujours retourner un seul élément racine. Si vous essayez de retourner plusieurs éléments « frères », vous obtiendrez une erreur :

// ❌ Erreur : deux éléments racine
function Entete() {
return (
<h1>Avis et alertes</h1>
<p>Ville de Montréal</p>
);
}

Option 1 : un Fragment (conseillé)

function Entete() {
return (
<>
<h1>Avis et alertes</h1>
<p>Ville de Montréal</p>
</>
);
}

Option 2 : un élément englobant

function Entete() {
return (
<div>
<h1>Avis et alertes</h1>
<p>Ville de Montréal</p>
</div>
);
}

Le Fragment (<>...</>) permet de grouper des éléments sans ajouter de balise supplémentaire au DOM. Utilisez-le quand un <div> serait superflu.

Les props

Un composant devient réutilisable quand son contenu peut varier. Les props (propriétés) servent à transmettre de l’information d’un composant parent à un composant enfant.

Exemple minimal

Parent.jsx
import Enfant from './Enfant';
function Parent() {
const message = "Bonjour depuis le parent !";
return (
<div>
<h1>Composant Parent</h1>
<Enfant message={message} />
</div>
);
}
export default Parent;
Enfant.jsx
function Enfant(props) {
return <p>{props.message}</p>;
}
export default Enfant;

Le parent passe message comme attribut JSX ; l’enfant le reçoit dans l’objet props et l’affiche.

Destructuration des props

La forme props.message fonctionne, mais on préfère souvent destructurer les props directement dans la signature de la fonction :

Enfant.jsx
function Enfant({ message }) {
return <p>{message}</p>;
}

Cette écriture est plus concise et rend visibles d’un coup d’œil les props attendues par le composant.

On peut aussi définir des valeurs par défaut :

function Bouton({ libelle = "Cliquez", actif = true }) {
return <button disabled={!actif}>{libelle}</button>;
}

Afficher une liste de composants

Dans une application réelle, on affiche rarement un élément seul — on affiche une liste. Pour cela, on utilise .map() pour transformer un tableau de données en un tableau d’éléments JSX.

function CarteAlerte({ titre, description }) {
return (
<div className="carte-alerte">
<h2>{titre}</h2>
<p>{description}</p>
</div>
);
}
function ListeAlertes() {
const alertes = [
{ id: 1, titre: 'Avis d\'ébullition', description: 'Ahuntsic-Cartierville' },
{ id: 2, titre: 'Fermeture de piscine', description: 'Le Plateau-Mont-Royal' },
{ id: 3, titre: 'Travaux de voirie', description: 'Villeray' }
];
return (
<div className="liste-alertes">
{alertes.map(alerte => (
<CarteAlerte
key={alerte.id}
titre={alerte.titre}
description={alerte.description}
/>
))}
</div>
);
}
export default ListeAlertes;

Props vs état

Avant de plonger dans le compteur, posons la distinction :

PropsÉtat (state)
SourceFournies par le parentInternes au composant
MutabilitéImmutablesMutables via setXxx
Effet sur le renduChange quand le parent passe de nouvelles valeursChange quand setXxx est appelé

Les props sont utilisées pour configurer un composant (le parent dit « voici tes paramètres »). L’état est utilisé pour mémoriser quelque chose qui peut évoluer pendant la vie du composant (valeur d’un champ, onglet actif, panier, etc.).

Construire un compteur

Valeur : 0

Nous allons construire ce compteur par étapes pour bien voir pourquoi useState est nécessaire.

Étape 1 : La structure

function Compteur() {
let compteur = 0;
return (
<div>
<p>Valeur : {compteur}</p>
<button>Incrémenter</button>
</div>
);
}

Un composant statique. Le bouton existe mais ne fait rien.

Étape 2 : Ajouter un gestionnaire d’événement

function Compteur() {
let compteur = 0;
const handleClick = () => {
console.log("Click!");
compteur += 1;
console.log("Valeur en mémoire :", compteur);
};
return (
<div>
<p>Valeur : {compteur}</p>
<button onClick={handleClick}>Incrémenter</button>
</div>
);
}

Étape 3 : Utiliser useState

import { useState } from "react";
function Compteur() {
const [compteur, setCompteur] = useState(0);
const handleClick = () => {
setCompteur(compteur + 1);
};
return (
<div>
<p>Valeur : {compteur}</p>
<button onClick={handleClick}>Incrémenter</button>
</div>
);
}

Décomposons :

const [compteur, setCompteur] = useState(0);
  • compteur : la valeur actuelle de l’état.
  • setCompteur : la fonction qui met à jour cet état.
  • useState(0) : la valeur initiale.

Quand setCompteur(...) est appelée, React re-rend automatiquement le composant avec la nouvelle valeur. C’est ce re-rendu qui met l’affichage à jour, contrairement à l’étape 2.

Étape 4 : Rendre le composant configurable

import { useState } from "react";
function Compteur({ valeurInitiale = 0, pas = 1 }) {
const [compteur, setCompteur] = useState(valeurInitiale);
return (
<div>
<p>Valeur : {compteur}</p>
<button onClick={() => setCompteur(compteur + pas)}>
Incrémenter
</button>
</div>
);
}

Deux choses à remarquer :

  1. Le composant accepte maintenant des props (valeurInitiale, pas) avec des valeurs par défaut.
  2. Le gestionnaire d’événement est défini en ligne (() => setCompteur(...)) — une écriture courante quand la logique est courte.

Étape 5 : Utilisation

App.jsx
import Compteur from "./Compteur";
function App() {
return (
<div>
<h1>Mon Application React</h1>
<Compteur valeurInitiale={500} pas={10} />
</div>
);
}
export default App;

Erreurs communes à éviter

ErreurSymptômeCorrection
Composant en minusculeLe composant ne s’affiche pas, pas d’erreur claire<Compteur /> et non <compteur />
Modifier un prop (props.x = ...)Comportement imprévisible, avertissementLes props sont en lecture seule — demander au parent
Modifier l’état directement (compteur += 1)L’affichage ne se met pas à jourToujours utiliser setCompteur(...)
Oublier key dans un .map()Avertissement dans la console, bogues de renduAjouter key={item.id}
Retourner plusieurs éléments sans racineErreur de compilationEnglober dans <>...</> ou <div>
Appeler setXxx dans le corps du composantBoucle infinie de rendusNe mettre à jour l’état que dans un gestionnaire d’événement ou un useEffect

Récapitulatif

  1. Un composant = une fonction qui retourne du JSX. Son nom commence par une majuscule.
  2. Les props permettent de transmettre des données du parent vers l’enfant. Elles sont immuables.
  3. L’état (useState) permet à un composant de mémoriser des valeurs qui évoluent dans le temps. Le modifier déclenche un nouveau rendu.
  4. La composition (composants dans composants) est le mécanisme de base pour construire une application.
  5. Les listes se rendent avec .map() et une key unique par élément.
  6. Les Fragments (<>...</>) permettent de regrouper plusieurs éléments sans balise parasite.

Exercices

  1. Greeting. Créez un composant Greeting qui affiche Bonjour, <nom> !. Le nom est passé en prop. Ajoutez une valeur par défaut « visiteur ».
  2. Compteur bidirectionnel. Étendez le Compteur pour y ajouter un bouton « Décrémenter » et un bouton « Réinitialiser ».
  3. Liste d’arrondissements. Créez un composant ListeArrondissements qui reçoit un tableau d’arrondissements en prop et affiche chacun dans un composant Arrondissement.
  4. Aperçu d’alerte. Créez un composant ApercuAlerte qui reçoit titre, arrondissement, sujet et dateEmission en props, et les affiche dans une carte stylisée. Ce composant vous servira dans le projet 1.

Ressources