DEV Community

Cover image for Partage de données entre composants RiotJS avec Mitt (émetteur d'évènements)
Steeve
Steeve

Posted on

Partage de données entre composants RiotJS avec Mitt (émetteur d'évènements)

Cet article traite de l'utilisation des événements avec Mitt entre plusieurs composants Riot pour partager des données.

Avant de commencer, assurez-vous d'avoir une application de base RiotJS, ou lisez mes articles précédents.

Je suppose que vous avez une compréhension fondamentale de Riot ; cependant, n'hésitez pas à vous référer à la documentation si nécessaire : https://riot.js.org/documentation/

Il existe trois méthodes pour partager des données entre composants :

  1. Utiliser les propriétés Riot props pour passer des valeurs à un composant enfant. Le composant enfant doit émettre des événements vers le composant parent si une action se produit, comme un clic ou un changement d'entrée. Dans ce cas, la portée de communication est limitée: du composant parent vers l'enfant et de l'enfant vers le parent.
  2. Utiliser un gestionnaire d'état (state manager), comme Pinia pour Vuejs ou Redux pour React, pour partager un état entre les composants/pages. La portée de communication est globale: tous les composants peuvent voir et modifier les états.

La dernière méthode utilise un émetteur d'événements comme Mitt, un modèle de messagerie appelé Pub/Sub : un ou plusieurs éditeurs émettent les données, et les abonnés les traite sans savoir quels sont les éditeurs. Cet article reproduit le modèle entre plusieurs composants RiotJS ! L'objectif est de :

  • Étape 1 : Créer une application Riot de base affichant un composant Modal de Confirmation lors du clic sur un bouton. Le bouton est l'éditeur, et le Modal est l'abonné.
  • Étape 2 : Lorsque le modal est visible et soit validé soit annulé, il émet un événement vers l'éditeur d'origine. Dans ce cas, le Modal est l'éditeur, et le Bouton est l'abonné.

Animation of a Riot confirmation modal opening due to a Mitt event

Base de l'émetteur

Commencez par installer Mitt dans votre projet Riot/Vite :

$ npm install mitt
Enter fullscreen mode Exit fullscreen mode

Créez un fichier nommé events.js, puis chargez Mitt et instanciez-le :

/** EVENT BUS **/
import mitt from 'mitt'

const _bus = mitt();

export default _bus;
Enter fullscreen mode Exit fullscreen mode

Composant de Dialogue (Abonné)

Créez un composant de dialogue nommé c-dialog.riot. La base du composant est tirée de mon article précédent Créer un composant de dialogue avec RiotJS:

<c-dialog>
    <dialog class="{state.active ? 'active ' : null}">
        <h5>{ state.title }</h5>
        <div>
            { state.message }
        </div>
        <nav class="right-align no-space">
            <button onclick={ toggleModal } class="transparent link">{ state.cancel }</button>
            <button onclick={ validateAction } class="transparent link">{ state.validate }</button>
        </nav>
    </dialog>
       <script>
       import events from './events.js'

        export default {
            state: {
                active: false,
                title: 'Confirmation',
                message: 'Are you sure?',
                callbackID : '',
                args : true,
                validate : 'Validate',
                cancel: "Cancel"
            },
            onMounted() {
                events.on('open-modal-validation', this.openModalValidation);
            },
            onUnmounted() {
                events.off('open-modal-validation', this.openModalValidation);
            },
            validateAction () {
                if (this.state.callbackID) {
                    events.emit(this.state.callbackID, this?.state?.args);
                }
                this.toggleModal();
            },
            openModalValidation ({ message, callbackID, args }) {
                this.update({
                    message,
                    callbackID,
                    args: args ?? true
                })
                this.toggleModal();
            },
            toggleModal() {
                this.update({ active: !this.state.active })
                if (this.state.active === true) {
                    console.log("Modal opened.")
                } else {
                    console.log("Modal closed.")
                }
            }
        }
    </script>
</c-dialog>
Enter fullscreen mode Exit fullscreen mode

Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/c-dialog.riot

Détails du code :

  • Le modal est visible uniquement si state.active est true.
  • Le dialogue peut être personnalisé avec des données personnalisées, stockées dans l'objet State Riot.
  • Le bus d'événements est chargé avec import events from './events.js'.
  • Lorsque le composant est monté, il s'abonne à l'événement open-modal-validation grâce à events.on('open-modal-validation', this.openModalValidation);. Lorsque l'événement nommé open-modal-validation se produit, il exécute la fonction openModalValidation.
  • La fonction openModalValidation bascule la visibilité du modal en définissant state.active sur true. En même temps, elle recueille des données supplémentaires telles qu'un state.message personnalisé et un state.callbackID.
  • Si le bouton de validation est cliqué, il émet un événement vers state.callback avec des arguments personnalisés state.args ; cela peut être un objet ou une chaîne de caractères.
  • Si le bouton d'annulation est cliqué, le modal est désactivé en définissant state.active sur false.

Composant Index (Éditeur)

Enfin, chargez le Dialogue et un Bouton dans un fichier index.riot:

<index-riot>
    <div style="width:600px;padding:20px;">
        <c-dialog />
        <c-button onclick={ confirmDeletion }> Delete File </c-button>
    </div>
    <script>
        import cButton from "../../components/c-button.riot";
        import cSnackbar from "../../components/c-snackbar.riot";
        import cDialog from "./c-dialog.riot";

        import events from "./events.js"

        export default {
            components: {
                cButton,
                cDialog
            },
            onMounted() {
                events.on('delete-file', this.deleteFile);
            },
            onUnmounted() {
                events.off('delete-file', this.deleteFile);
            },
            state: {
                active: false
            },
            confirmDeletion () {
                events.emit('open-modal-validation', { 
                    message: "Do you confirm you want to delete the file \"2024-04-invoice.pdf\"?",
                    callbackID: 'delete-file'
                });
            },
            deleteFile () {
                console.log("FILE DELETED! 🗑️");
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/index.riot

Détails du code :

  1. Les composants sont importés avec import cDialog from "./components/c-dialog.riot"; puis chargés dans l'objet Riot components:{}.
  2. Le composant dialog est instancié avec dans le HTML.
  3. L'émetteur d'événements, Mitt, est chargé avec import events from "./events.js".
  4. Lors du clic sur le bouton, la fonction confirmDeletion est exécutée et émet l'événement "open-modal-validation". Le Dialogue est maintenant visible ! Simultanément, un message personnalisé et l'ID de l'abonné "delete-file" sont passés en tant qu'arguments pour l'événement.
  5. Lorsque le bouton du dialogue est cliqué, un événement est émis, en utilisant state.callbackID comme destinataire de l'abonné.

Cette méthode peut être étendue en créant plus de boutons (éditeurs) émettant vers un seul Dialogue (abonné), par exemple :

Three buttons are opening the same Dialog box to confirm deletions actions, either: a file, a bucket or an account

Voici le code de index.riot pour reproduire la démo GIF :

<index-riot>
    <div style="width:600px;padding:50px;">
        <c-dialog />
        <c-button onclick={ confirmDeletion }> Delete File </c-button>
        <c-button onclick={ confirmDeletionBucket }> Delete Bucket </c-button>
        <c-button error={ true } onclick={ confirmDeletionAccount }> Delete Account </c-button>
    </div>
    <script>
        import cButton from "../../components/c-button.riot";
        import cSnackbar from "../../components/c-snackbar.riot";
        import cDialog from "./c-dialog.riot";

        import events from "./events.js"

        export default {
            components: {
                cButton,
                cDialog
            },
            onMounted() {
                events.on('delete-file', this.deleteFile);
                events.on('delete-bucket', this.deleteBucket);
                events.on('delete-account', this.deleteAccount);
            },
            onUnmounted() {
                events.off('delete-file', this.deleteFile);
                events.off('delete-bucket', this.deleteBucket);
                events.off('delete-account', this.deleteAccount);
            },
            state: {
                active: false
            },
            getRandomNumber() {
                return Math.floor(Math.random() * Date.now())
            },
            confirmDeletion () {
                events.emit('open-modal-validation', { 
                    message: "Do you confirm you want to delete the File \"2024-04-invoice.pdf\"?",
                    callbackID: 'delete-file',
                    args: "file-" + this.getRandomNumber()
                });
            },
            confirmDeletionBucket() {
                events.emit('open-modal-validation', { 
                    message: "Are you sure you want to delete this S3 bucket \"2024-assets\"? This action is irreversible.",
                    callbackID: 'delete-bucket',
                    args: "bucket-" + this.getRandomNumber()
                });
            },
            confirmDeletionAccount() {
                events.emit('open-modal-validation', { 
                    message: "Are you sure you want to proceed with deleting your account? This action is irreversible and will permanently remove all your personal data associated with the account.",
                    callbackID: 'delete-account',
                    args: "account-" + this.getRandomNumber()
                });
            },
            deleteFile (id) {
                console.log("%c File deleted! ID: " + id, 'background: #222; color: #bada55');
            },
            deleteBucket (id) {
                console.log("%c Bucket deleted! ID: " + id, 'background: #222; color: #ff00ff');
            },
            deleteAccount (id) {
                console.log("%c Account deleted! ID: " + id, 'background: #222; color: #ff0000');
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/index.riot

Détails du code :

  1. Dans le onMounted Riot Hook, trois événements sont écoutés et redirigés vers trois fonctions : deleteFile, deleteBucket ou deleteAccount. Dans une application de production, cela pourrait être un appel API pour supprimer une ressource (fichier ou élément dans une base de données).
  2. Trois boutons sont créés dans le HTML, et chaque clic émet l'événement open-modal-validation et passe un message personnalisé, un ID de rappel et un argument :
    • Le callbackID est un événement à émettre lorsque le bouton de validation est cliqué.
    • Les args sont utilisés pour passer un ID de ressource d'une API ou d'une base de données, comme un ID de fichier ou un ID de compte. Dans notre cas, un ID aléatoire est passé en tant qu'argument.
  3. Lorsque qu'un bouton du composant Dialogue est validé, il émet callbackID en tant qu'événement, et la fonction correspondante est exécutée pour supprimer le fichier, le bucket ou le compte.
  4. Lorsque le composant est retiré de la page web, tout événement doit être écouté : Lorsque le onUnmounted](https://riot.js.org/api/#component-object) Riot hook est exécuté, event.off supprime l'abonnement à l'événement.

Conclusion

Au lieu de créer un Dialogue pour chaque bouton, un seul Dialogue suffit pour plusieurs boutons avec le modèle pub/sub. Maintenant, un composant peut écouter et émettre des événements pour communiquer des données basées sur des actions. Voilà 🎉

Passez une excellente journée ! Santé 🍻

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.