vendredi 25 septembre 2026

À propos

Contact

Blocs Gutenberg

Découper un gros bloc en composants React réutilisables

Chaque nouveau bloc réécrivait les mêmes contrôles d'inspecteur. Voici comment une équipe a extrait des composants partagés et géré leurs props communes.

Par Clément Hadrot • 25 mars 2025 • 4 min de lecture • Aucun commentaire
Découper un gros bloc en composants React réutilisables

Sur le plugin de blocs maison de la Librairie Miroir, un réseau de librairies indépendantes, chacun des douze blocs comportait son propre contrôle de sélection de couleur d’accent, sa propre gestion de l’espacement interne, et son propre sélecteur d’image de fond, tous réécrits séparément avec des variations mineures d’un bloc à l’autre. Ajouter une option à l’un de ces contrôles supposait de la répliquer manuellement dans onze autres fichiers, avec le risque d’oubli que cela comporte.

Repérer les contrôles réellement dupliqués

La première étape n’a pas été technique mais d’inventaire : parcourir les douze composants InspectorControls et lister ce qui se répétait à l’identique ou presque. Trois candidats sont ressortis nettement : le sélecteur de couleur d’accent, le contrôle d’espacement, et le sélecteur d’image de fond avec son bouton de suppression. D’autres contrôles, spécifiques à un seul bloc, n’ont volontairement pas été extraits : l’extraction prématurée d’un contrôle utilisé une seule fois ajoute une indirection sans bénéfice.

Structure du dossier de composants partagés

Un dossier src/components a été créé en parallèle du dossier src/blocks, avec une arborescence organisée par contrôle plutôt que par bloc consommateur :

src/
├── blocks/
│   ├── bandeau-evenement/
│   ├── fiche-auteur/
│   └── selection-rayon/
└── components/
    ├── selecteur-couleur-accent/
    │   └── index.js
    ├── controle-espacement/
    │   └── index.js
    └── selecteur-image-fond/
        └── index.js

Des props explicites plutôt qu’un objet attributs entier

Le premier réflexe, tentant, aurait été de passer l’objet attributes et setAttributes du bloc tel quel à chaque composant partagé. Cette solution a été écartée volontairement : elle couple fortement le composant partagé au schéma d’attributs de chaque bloc consommateur, empêchant par exemple de nommer différemment l’attribut de couleur d’un bloc à l’autre. La règle retenue impose des props explicites :

function SelecteurCouleurAccent({ valeur, onChange, label = 'Couleur d\'accent' }) {
  return (
    <ColorPalette
      value={valeur}
      onChange={onChange}
      disableCustomColors={false}
      aria-label={label}
    />
  );
}

// Dans un bloc consommateur :
<SelecteurCouleurAccent
  valeur={attributes.couleurAccent}
  onChange={(valeur) => setAttributes({ couleurAccent: valeur })}
/>
L'essentiel à retenir : Un composant partagé par contrôle récurrent, pas par bloc ; Des props explicites plutôt qu'un objet attributs entier passé tel quel ; Un dossier components séparé du dossier blocks

Chaque bloc reste ainsi seul responsable du nom de ses propres attributs et de la logique de mise à jour, tandis que le composant partagé ne connaît que l’interface minimale dont il a besoin : une valeur, un gestionnaire de changement, un libellé optionnel.

Gérer les variations mineures entre blocs

Le contrôle d’espacement, utilisé sur huit des douze blocs, présentait une variation : certains blocs autorisaient un espacement négatif (pour un chevauchement visuel volontaire), d’autres non. Plutôt que de créer deux composants quasi identiques, une prop autoriserNegatif a été ajoutée, avec une valeur par défaut à false :

function ControleEspacement({ valeur, onChange, autoriserNegatif = false }) {
  return (
    <RangeControl
      label="Espacement"
      value={valeur}
      onChange={onChange}
      min={autoriserNegatif ? -40 : 0}
      max={80}
    />
  );
}

Ce que cette extraction n’a pas cherché à résoudre

  • Elle ne réorganise pas le plugin en plusieurs paquets publiés séparément, une échelle différente déjà couverte pour les projets qui gèrent plusieurs plugins de blocs en monorepo.
  • Elle n’introduit aucune abstraction générique du type « constructeur de panneau d’inspecteur » : chaque bloc compose ses propres composants partagés dans l’ordre qui lui convient, sans configuration déclarative supplémentaire.
  • Elle ne touche pas au rendu front des blocs, uniquement à l’expérience d’édition.

Un composant partagé qui connaît le nom des attributs d’un bloc en particulier n’est pas partagé, il est simplement dupliqué avec un niveau d’indirection en plus.

Notre verdict

L’extraction de trois composants a réduit le code des douze blocs de la Librairie Miroir de plus de six cents lignes cumulées, et surtout a rendu triviale l’ajout d’une option au sélecteur de couleur : une seule modification, propagée automatiquement à tous les blocs consommateurs, là où il aurait fallu auparavant modifier onze fichiers à la main.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi