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
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
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
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 └── FooterPoint d’entrée de l’application
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 racinefunction 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
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;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 :
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) | |
|---|---|---|
| Source | Fournies par le parent | Internes au composant |
| Mutabilité | Immutables | Mutables via setXxx |
| Effet sur le rendu | Change quand le parent passe de nouvelles valeurs | Change 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 :
- Le composant accepte maintenant des props (
valeurInitiale,pas) avec des valeurs par défaut. - Le gestionnaire d’événement est défini en ligne (
() => setCompteur(...)) — une écriture courante quand la logique est courte.
Étape 5 : Utilisation
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
| Erreur | Symptôme | Correction |
|---|---|---|
| Composant en minuscule | Le composant ne s’affiche pas, pas d’erreur claire | <Compteur /> et non <compteur /> |
Modifier un prop (props.x = ...) | Comportement imprévisible, avertissement | Les props sont en lecture seule — demander au parent |
Modifier l’état directement (compteur += 1) | L’affichage ne se met pas à jour | Toujours utiliser setCompteur(...) |
Oublier key dans un .map() | Avertissement dans la console, bogues de rendu | Ajouter key={item.id} |
| Retourner plusieurs éléments sans racine | Erreur de compilation | Englober dans <>...</> ou <div> |
Appeler setXxx dans le corps du composant | Boucle infinie de rendus | Ne mettre à jour l’état que dans un gestionnaire d’événement ou un useEffect |
Récapitulatif
- Un composant = une fonction qui retourne du JSX. Son nom commence par une majuscule.
- Les props permettent de transmettre des données du parent vers l’enfant. Elles sont immuables.
- L’état (
useState) permet à un composant de mémoriser des valeurs qui évoluent dans le temps. Le modifier déclenche un nouveau rendu. - La composition (composants dans composants) est le mécanisme de base pour construire une application.
- Les listes se rendent avec
.map()et unekeyunique par élément. - Les Fragments (
<>...</>) permettent de regrouper plusieurs éléments sans balise parasite.
Exercices
- Greeting. Créez un composant
Greetingqui afficheBonjour, <nom> !. Le nom est passé en prop. Ajoutez une valeur par défaut « visiteur ». - Compteur bidirectionnel. Étendez le
Compteurpour y ajouter un bouton « Décrémenter » et un bouton « Réinitialiser ». - Liste d’arrondissements. Créez un composant
ListeArrondissementsqui reçoit un tableau d’arrondissements en prop et affiche chacun dans un composantArrondissement. - Aperçu d’alerte. Créez un composant
ApercuAlertequi reçoittitre,arrondissement,sujetetdateEmissionen props, et les affiche dans une carte stylisée. Ce composant vous servira dans le projet 1.
Ressources
- Guide React — Décrire le UI à l’aide de composants
- Guide React — Gérer l’état
- Référence React — Composant
- React DevTools — extension pour inspecter l’arbre des composants dans le navigateur.