Passer des arguments dans vos composants Gutenberg

Petite astuce très simple pour vous permettre d’organiser vos blocs Gutenberg avec vos propres composants.

Je me suis dit que cela pourrait servir car ce n’est pas documenté directement et c’est normal car cela tient plus aux basiques de React qu’à Gutenberg.

Disclaimer

J’utilise le JSX dans mes codes et mes démos, pas d’ES5.

Si ces termes ne vous disent rien vous pouvez lire cette petite introduction à React.

Télécharger l’exemple

Voici un lien pour télécharger l’exemple, au complet, codé dans ce billet :

Explications de l’exemple

Pour cette démo j’ai pris cette base de code par Riad Benguella.

Installation et démarrage

Avant tout activer le plugin starter sur l’installation locale.

Supposons que l’on ait besoin de rajouter des éléments à notre bloc. On pourrait tout à fait les ajouter à la suite dans le même fichier /src/index.js

Mais bien souvent on va devoir découper ne serait-ce que pour faciliter la maintenance de notre code et éviter d’avoir à éditer un fichier de 800 lignes de js à chaque fois. Commençons par ajouter des champs à cette base de code. Voici le code brut de base fourni par le starter :

registerBlockType("wp-js-plugin-starter/hello-world", {
  title: "Hello World",
  description: "Just another Hello World block",
  icon: "admin-site",
  category: "common",

  edit: function() {
    return <p>Hello Editor</p>;
  },

  save: function() {
    return <p>Hello Frontend</p>;
  }
});

Pour se faire, on va déjà :

  1. lancer un npm install pour récupérer les dépendances
  2. lancer un npm start (~compilateur et watcher)

Cela va créer deux fichiers dans le répertoire /dist/ de notre plugin de démo et nous permettre de régénérer les fichiers compilés à chaque fois.

Attribut personnalisé et champ texte

On va avoir besoin d’une nouvelle dépendance pour utiliser le composant TextControl de Gutenberg. On peut d’abord ajouter “wp-components” en dépendance dans le fichier wp-js-plugin-starter.php comme suit :

wp_register_script(
    'wp-js-plugin-starter',
    wp_js_plugin_starter_url( 'dist/index.js' ),
    array( 'wp-blocks', 'wp-element', 'wp-components' ),
    '1.0.1'
);

Ensuite dans le fichier /src/index.js on va pouvoir appeler ce nouveau composant pour ajouter un champ texte :

const {createElement,Fragment} = wp.element;
const {registerBlockType} = wp.blocks;
const {TextControl} = wp.components;

registerBlockType("wp-js-plugin-starter/hello-world", {
    title: "Hello World",
    description: "Just another Hello World block",
    icon: "admin-site",
    category: "common",
    attributes: {
        extraText: {
            type: "string",
            default: "Lorem ipsum..."
        },
    },

    edit({attributes, setAttributes}) {
        const {extraText} = attributes;
        const updateExtraText = extraText => setAttributes({extraText});
        return (
            <Fragment>
                <TextControl
                    value={extraText}
                    onChange={updateExtraText}
                />
                <p>Hello Editor</p>
            </Fragment>
        );
    },

    save: function () {
        return <p>Hello Frontend</p>;
    }
});

Vous noterez l’utilisation de “Fragment” cela nous sert à embarquer deux éléments adjacents sinon React nous sortirait une erreur. L’avantage par rapport à la div est qu’il ne rajoute pas de markup HTML inutilement.

Maintenant notre bloc ressemble à ça et permet de sauvegarder un champ texte :

On va ensuite supprimer ce bloc de notre écran de modification et en redémarrer un autre car on va changer du HTML et Gutenberg n’aime pas ces différences, il nous sortirait une erreur et un message du type “voulez-vous convertir en bloc html”.

Composant maison

Les composants sont le coeur même de React et Gutenberg, ils permettent, entre autres, de bien découper son code en vue de le réutiliser. Ici on va faire un composant ultra basique et probablement qu’on n’irait pas jusque là pour un seul petit champ texte mais quand les champs s’accumulent c’est pratique et cela permettra de voir à quel point il est simple de passer des arguments.

On va commencer par rajouter notre fichier de composant /src/components/text/index.js et mettre à l’intérieur :

const {createElement,Component} = wp.element;

class ComponentText extends Component {
    render() {
        return (
            <p>Hello Guys !</p>
        );
    }

}

export const TheText = ComponentText;

et l’appeler dans notre fichier /src/index.js comme suit :

const {createElement,Fragment} = wp.element;
const {registerBlockType} = wp.blocks;
const {TextControl} = wp.components;

import {TheText} from "./components/text";

registerBlockType("wp-js-plugin-starter/hello-world", {
    title: "Hello World",
    description: "Just another Hello World block",
    icon: "admin-site",
    category: "common",
    attributes: {
        extraText: {
            type: "string",
            default: "Lorem ipsum..."
        },
    },

    edit({attributes, setAttributes}) {
        const {extraText} = attributes;
        const updateExtraText = extraText => setAttributes({extraText});
        return (
            <Fragment>
                <TextControl
                    value={extraText}
                    onChange={updateExtraText}
                />
                <TheText />
            </Fragment>
        );
    },

    save: function () {
        return <TheText />;
    }
});

Attention ne cherchez pas à tester encore le code dans l’editeur, il nous manque la dernière partie et la plus importante puisque c’est le sujet de ce billet 🙂

Si ces lignes vous perturbent :

  edit({attributes, setAttributes}) {
        const {extraText} = attributes;

Cela s’appelle destructurer, c’est juste un raccourci pour en écrire moins et récupérer directement l’attribut qui nous intéresse.

Passer les arguments à notre composant

On va d’abord transmettre le paramètre que l’on vient d’ajouter vers le composant TheText :

const {createElement,Fragment} = wp.element;
const {registerBlockType} = wp.blocks;
const {TextControl} = wp.components;

import {TheText} from "./components/text";

registerBlockType("wp-js-plugin-starter/hello-world", {
    title: "Hello World",
    description: "Just another Hello World block",
    icon: "admin-site",
    category: "common",
    attributes: {
        extraText: {
            type: "string",
            default: "Lorem ipsum..."
        },
    },

    edit({attributes, setAttributes}) {
        const {extraText} = attributes;
        const updateExtraText = extraText => setAttributes({extraText});
        return (
            <Fragment>
                <TextControl
                    value={extraText}
                    onChange={updateExtraText}
                />
                <TheText text={extraText}/>
            </Fragment>
        );
    },

    save({attributes}){
        const {extraText} = attributes;
        return <TheText text={extraText}/>;
    }
});

puis il suffira de l’appeler comme suit dans notre fichier de composant :

const {createElement,Component} = wp.element;

class ComponentText extends Component {
    render() {
        return (
            <p>{this.props.text}</p>
        );
    }

}

export const TheText = ComponentText;

Comme on est dans une classe on doit faire appel au fameux this et ensuite il suffit de reprendre ce qu’on a écrit plus haut, à savoir la props text :

<TheText text={extraText}/>

#tips : Si jamais vous avez beaucoup d’arguments à passer comme ça, ne vous embêtez pas, passer directement attributes :

<TheText att={attributes}/>

Conclusion

On va pouvoir gérer notre composant directement dans un fichier dédié car à chaque rendu on lui passe désormais le paramètre souhaité. Ainsi le résultat sera géré à un seul endroit dans notre exemple à la fois pour la méthode edit et la méthode save.