
Compound Components (Composants composés) : API flexible et expressive
Maîtrisez le pattern Compound Components dans React pour construire des composants complexes avec une API déclarative et intuitive. Apprenez à partager un état implicite via Context pour une meilleure encapsulation et flexibilité.
Qu'est-ce que le pattern Compound Components ?
Le pattern Compound Components (ou Composants Composés) est une technique avancée en React qui permet de créer des relations explicites entre plusieurs composants, leur permettant de travailler ensemble pour gérer un état partagé et une logique commune, tout en offrant une API très expressive et flexible à l'utilisateur final. L'idée fondamentale est de regrouper plusieurs composants qui forment une unité logique, où le composant parent principal orchestre l'état et le comportement, et les composants enfants contribuent à l'interface utilisateur globale.
Pensez aux éléments HTML natifs comme `
Au lieu d'avoir un seul composant monolithique avec une multitude de props de configuration complexes, le pattern Compound Components décompose la fonctionnalité en plusieurs petits composants spécialisés qui communiquent implicitement entre eux, le plus souvent via l'API Context de React.
Motivation et avantages clés
L'utilisation de ce pattern est motivée par le besoin de créer des composants réutilisables qui sont à la fois puissants et faciles à utiliser. Pour des UI complexes comme les systèmes d'onglets, les accordéons, les menus déroulants ou les listes personnalisées, gérer toutes les variations via un seul composant et ses props peut devenir rapidement ingérable.
Les principaux avantages des Compound Components sont :
1. API Expressive et Sémantique : La structure du JSX lors de l'utilisation du composant ressemble davantage à du HTML sémantique, rendant le code plus lisible et intuitif. L'intention est claire.
2. Flexibilité pour l'Utilisateur : Le consommateur du composant a plus de contrôle sur la structure et le rendu. Il peut choisir l'ordre des composants enfants, insérer d'autres éléments entre eux, ou appliquer des styles spécifiques plus facilement qu'avec une configuration basée uniquement sur les props.
3. Encapsulation de l'Etat : La logique et l'état complexes sont gérés en interne par le composant parent et partagés implicitement (via Context) avec les enfants concernés. L'utilisateur n'a pas besoin de gérer cet état lui-même ou de le faire transiter via des props (évite le prop drilling).
4. Séparation des Préoccupations : Chaque composant enfant a une responsabilité unique et bien définie, ce qui améliore la maintenabilité et la testabilité de l'ensemble.
Implémentation avec l'API Context
La méthode moderne et privilégiée pour implémenter les Compound Components repose sur l'API Context de React (`React.createContext` et `useContext`). Le composant parent principal crée un contexte et y expose l'état partagé (par exemple, l'index de l'onglet actif, l'état ouvert/fermé d'un accordéon) ainsi que les fonctions permettant de modifier cet état (par exemple, `setActiveTab`, `toggleItem`).
Les composants enfants spécialisés utilisent ensuite le hook `useContext` pour accéder à cet état partagé et aux fonctions dont ils ont besoin pour leur propre logique de rendu ou d'interaction, sans que le composant parent ait besoin de leur passer explicitement ces informations via les props.
Voici l'architecture typique :
1. Créer un Contexte : Définir un contexte spécifique pour le groupe de composants (`const MyComponentContext = React.createContext();`).
2. Fournir le Contexte : Le composant parent principal enveloppe ses `children` dans le `Provider` de ce contexte, en passant l'état et les actions nécessaires dans la `value` du Provider.
3. Consommer le Contexte : Les composants enfants utilisent `useContext(MyComponentContext)` pour récupérer les données et les fonctions partagées depuis le Provider.
4. Attacher les enfants au parent : Les composants enfants sont généralement définis comme des propriétés statiques du composant parent (par exemple, `Tabs.TabList`, `Tabs.Tab`) pour renforcer l'idée qu'ils appartiennent à cet ensemble et pour faciliter leur importation et leur utilisation.
Exemple : Un système d'onglets (Tabs)
Créons un exemple simple de `Tabs` en utilisant le pattern Compound Components et Context.
import React, { useState, useContext, createContext } from 'react';
// 1. Créer le Contexte
const TabsContext = createContext();
// --- Composant Parent Principal ---
function Tabs({ children, defaultActiveIndex = 0 }) {
const [activeIndex, setActiveIndex] = useState(defaultActiveIndex);
// Valeurs à partager via le contexte
const contextValue = {
activeIndex,
setActiveIndex,
};
// 2. Fournir le contexte aux enfants
return (
{children}
);
}
// --- Composant Enfant : Liste des titres d'onglets ---
function TabList({ children }) {
return {children};
}
// --- Composant Enfant : Titre d'un onglet cliquable ---
function Tab({ children, index }) {
// 3. Consommer le contexte
const { activeIndex, setActiveIndex } = useContext(TabsContext);
const isActive = index === activeIndex;
return (
);
}
// --- Composant Enfant : Conteneur des panneaux de contenu ---
function TabPanels({ children }) {
// On pourrait filtrer/cloner les enfants ici si nécessaire,
// mais pour cet exemple simple, on les rend directement.
return {children};
}
// --- Composant Enfant : Panneau de contenu d'un onglet ---
function TabPanel({ children, index }) {
// 3. Consommer le contexte
const { activeIndex } = useContext(TabsContext);
const isActive = index === activeIndex;
// Affiche le contenu seulement si l'onglet est actif
return isActive ? {children} : null;
}
// 4. Attacher les enfants comme propriétés statiques (bonne pratique)
Tabs.TabList = TabList;
Tabs.Tab = Tab;
Tabs.TabPanels = TabPanels;
Tabs.TabPanel = TabPanel;
// --- Utilisation du composant Tabs ---
function App() {
return (
Onglet 1
Onglet 2
Onglet 3
Contenu de l'onglet 1.
Contenu de l'onglet 2.
On peut mettre n'importe quel JSX ici.
Contenu de l'onglet 3.
);
}
export default App;Dans cet exemple, `Tabs` gère `activeIndex` et le partage via `TabsContext`. `Tab` utilise le contexte pour savoir s'il est actif et pour appeler `setActiveIndex` au clic. `TabPanel` utilise aussi le contexte pour déterminer s'il doit afficher son contenu. L'utilisation dans `App` est déclarative et facile à comprendre, ressemblant à une structure HTML sémantique.
Conclusion : Un pattern puissant pour des API robustes
Le pattern Compound Components est une technique de composition puissante dans React, particulièrement adaptée à la création de composants d'interface utilisateur complexes et interactifs. En s'appuyant principalement sur l'API Context, il permet de construire des API de composants qui sont à la fois flexibles pour le développeur consommateur et bien encapsulées en termes de gestion d'état interne.
Bien qu'il demande une compréhension plus approfondie de la composition et du contexte dans React, l'investissement en vaut la peine pour développer des bibliothèques de composants ou des éléments d'interface réutilisables qui sont à la fois robustes, maintenables et agréables à utiliser. C'est un excellent outil à ajouter à votre arsenal de développeur React pour concevoir des abstractions de composants de haute qualité.