Le WordPress d'aujourd'hui, décodé pour les développeurs

Blocs Gutenberg

compose() de @wordpress/compose combine plusieurs HOC pour un même bloc

Empiler withSelect, withDispatch et withInstanceId à la main produit vite un mur de parenthèses. La fonction compose() range cette pile proprement, dans l'ordre.

Par WordPress Développement • 24 avril 2020 • 4 min de lecture • Aucun commentaire
compose() de @wordpress/compose combine plusieurs HOC pour un même bloc

Quatre. C’est le nombre de composants d’ordre supérieur qu’un bloc un peu ambitieux accumule facilement en 2020 : withSelect pour lire des données, withDispatch pour les modifier, withInstanceId pour générer un identifiant unique, et parfois un HOC maison pour gérer un état local partagé. Écrits à la main, ces quatre appels s’imbriquent les uns dans les autres et produisent un empilement de parenthèses fermantes difficile à relire.

La fonction compose(), fournie par le paquet @wordpress/compose, résout ce problème d’écriture sans rien changer au comportement : elle prend une liste de fonctions et retourne une fonction unique qui les applique successivement au composant de départ.

Le problème que compose() résout

Sans compose(), appliquer trois HOC à un composant Edit ressemble à ceci :

export default withInstanceId(
    withDispatch( mapDispatchToProps )(
        withSelect( mapSelectToProps )( Edit )
    )
);

Chaque nouvelle couche ajoute un niveau d’imbrication et une parenthèse à retrouver en fin de ligne. Sur un bloc qui grossit avec le temps, ce style devient une source d’erreurs bêtes : une parenthèse oubliée, un HOC ajouté au mauvais niveau, un diff Git illisible.

La même chose avec compose()

L'essentiel à retenir : Empile plusieurs HOC sans imbrication visuelle ; Applique les fonctions de droite à gauche ; Reste compatible avec les hooks React sur le même composant

compose() aplati cette pile en une liste lisible de haut en bas :

import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';
import { withInstanceId } from '@wordpress/compose';

export default compose( [
    withSelect( mapSelectToProps ),
    withDispatch( mapDispatchToProps ),
    withInstanceId,
] )( Edit );

Chaque ligne du tableau correspond à un HOC appliqué, dans le même ordre qu’on les lirait dans une phrase. Ajouter ou retirer un comportement revient à ajouter ou supprimer une ligne, sans toucher à la structure d’ensemble.

L’ordre d’application compte

Un point mérite une attention particulière : compose() applique les fonctions dans l’ordre du tableau, mais chaque HOC enveloppe le résultat du précédent. Concrètement, le premier élément du tableau est le plus proche du composant final rendu, et le dernier élément est le plus extérieur. Sur l’exemple ci-dessus, withInstanceId reçoit un composant déjà enrichi par withDispatch, lui-même appliqué sur un composant déjà enrichi par withSelect.

Cet ordre a des conséquences pratiques : si withDispatch a besoin d’une prop injectée par withSelect (par exemple un identifiant d’article récupéré via une sélection), withSelect doit impérativement apparaître avant dans le tableau.

Composer avec ses propres fonctions

Rien n’empêche d’ajouter un HOC maison à la liste, tant qu’il respecte la même signature : une fonction qui prend un composant et retourne un composant.

const withMonHistorique = ( Composant ) => ( props ) => {
    // logique locale, indépendante du magasin de données
    return <Composant { ...props } />;
};

export default compose( [
    withSelect( mapSelectToProps ),
    withMonHistorique,
] )( Edit );

Cette souplesse explique pourquoi compose() reste répandu même sur des blocs qui n’utilisent pas uniquement les HOC fournis par WordPress.

Cohabitation avec les hooks React

  • compose() ne remplace pas les hooks React (useSelect, useDispatch), il cohabite avec eux.
  • Un composant enveloppé par compose() peut très bien appeler useState ou useEffect en interne.
  • Migrer un bloc existant se fait généralement HOC par HOC, pas d’un seul coup sur tout le fichier.
  • compose() vient à l’origine de la bibliothèque Redux et son fonctionnement (composition de fonctions de droite à gauche, au sens mathématique) est identique.

Ce qu’il faut retenir avant de choisir

Sur un bloc neuf en 2020, la question se pose légitimement : partir directement sur les hooks React, plus récents, ou continuer avec compose() et les HOC historiques ? La réponse dépend surtout de l’existant : sur un projet qui compte déjà plusieurs blocs écrits avec des HOC, garder la même convention facilite la maintenance. Sur un bloc entièrement neuf, les hooks réduisent le nombre de couches à lire.

Sur nos projets qui mélangent les deux générations de blocs, on documente systématiquement dans le fichier README du bloc quelle convention a été choisie, pour éviter qu’un nouveau développeur ne mélange les deux styles dans le même composant.

En résumé

compose() ne change rien au comportement d’un bloc, seulement sa lisibilité. Sur un bloc qui empile trois ou quatre comportements distincts, la différence entre un mur de parenthèses et une liste ordonnée se ressent dès la première relecture par un collègue. Le paquet @wordpress/compose reste au cœur de l’éditeur de blocs, hooks ou pas.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

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

Voir tous ses articles

Dans la même veine

À lire aussi