Passing values across the components using pub-sub (LWC)

Kapil ChauhanKapil Chauhan
4 min read

I came across a question in my recent interview : If we want to pass values from component A to B to C where all 3 components are not in a relationship and in a same page , what can be the best way ? pub-sub or Messaging -Service . After I try to implement the same , it turns out that both can do that job.

PubSub : So We will create 3 components one having Country picklist , 2nd State picklist and 3rd Capital picklist and try to send information when picklist values changes across these components .

Country Component

import { LightningElement, wire } from 'lwc';  
import { CurrentPageReference } from 'lightning/navigation'; 
import { fireEvent } from 'c/pubsub'; 
export default class MyPublisher extends LightningElement {
    @wire(CurrentPageReference) pageRef; 
    selectedCountry;
    options = [
        {label : 'India' , value :  'India'},
        {label : 'Canada' , value :  'Canada'}
    ]

    handlePicklistChange(event){
           this.selectedCountry = event.target.value;
           fireEvent(this.pageRef , "selectedCountry" ,this.selectedCountry );
    }

}

State Component

import { LightningElement , wire , track } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub'; 
import { CurrentPageReference } from 'lightning/navigation'; 
import { fireEvent } from 'c/pubsub'; 
export default class MySubscriber extends LightningElement {
    @track details; 
    @wire(CurrentPageReference) pageRef; 
    selectedCountry;
    selectedState;
    connectedCallback() { 
        registerListener("selectedCountry" , this.updateCountry , this);
    } 
    updateCountry(country){
          this.selectedCountry = country;
    }
    countryOptions = [
        {label : 'India' , value :  'India'},
        {label : 'Canada' , value :  'Canada'}
    ]
    stateOptions = [
        {label : 'Uttarakhand' , value :  'Uttarakhand'},
        {label : 'Bristish Columbia' , value :  'Bristish Columbia'}
    ]
    handleStateChange(event){
           this.selectedState = event.target.value;
           fireEvent(this.pageRef , "selectedState" ,this.selectedState );
    }
    disconnectedCallback() { 
        unregisterAllListeners(this); 
    } 
}

Capital Component

import { LightningElement , wire , track } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub'; 
import { CurrentPageReference } from 'lightning/navigation';

export default class CapitalComponent extends LightningElement {
    @track details; 
    @wire(CurrentPageReference) pageRef; 
    selectedCountry;
    selectedState;
    selectedCapital;
    connectedCallback() {      
        registerListener("selectedState" , this.updateState , this);
    } 
    updateCountry(country){
          this.selectedCountry = country;
    }
    updateState(state){
        this.selectedState = state;
    }
    capitalOptions = [
        {label : 'Dehradun' , value :  'Dehradun'},
        {label : 'Chandigarh' , value :  'Chandigarh'}
    ]
    handleCountryChange(event){
       this.selectedCountry = event.target.value;
    }
    handleStateChange(event){
           this.selectedState = event.target.value;
    }
    handleCapitalChange(event){
       this.selectedCapital = event.target.value;
    }
    disconnectedCallback() { 
        unregisterAllListeners(this); 
    } 
}

And also remember you need to create a pub-sub file : > so under LWC : create folder : pubsub : and a file : pubsub.js and paste below code

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

const events = {};
const samePageRef = (pageRef1, pageRef2) => {
    const obj1 = pageRef1.attributes;
    const obj2 = pageRef2.attributes;
    return Object.keys(obj1)
        .concat(Object.keys(obj2))
        .every(key => {
            return obj1[key] === obj2[key];
        });
};

/**
 * Registers a callback for an event
 * @param {string} eventName - Name of the event to listen for.
 * @param {function} callback - Function to invoke when said event is fired.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const registerListener = (eventName, callback, thisArg) => {
    // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
    if (!thisArg.pageRef) {
        throw new Error(
            'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property'
        );
    }

    if (!events[eventName]) {
        events[eventName] = [];
    }
    const duplicate = events[eventName].find(listener => {
        return listener.callback === callback && listener.thisArg === thisArg;
    });
    if (!duplicate) {
        events[eventName].push({ callback, thisArg });
    }
};
/**
 * Unregisters a callback for an event
 * @param {string} eventName - Name of the event to unregister from.
 * @param {function} callback - Function to unregister.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const unregisterListener = (eventName, callback, thisArg) => {
    if (events[eventName]) {
        events[eventName] = events[eventName].filter(
            listener =>
                listener.callback !== callback || listener.thisArg !== thisArg
        );
    }
};
/**
 * Unregisters all event listeners bound to an object.
 * @param {object} thisArg - All the callbacks bound to this object will be removed.
 */
const unregisterAllListeners = thisArg => {
    Object.keys(events).forEach(eventName => {
        events[eventName] = events[eventName].filter(
            listener => listener.thisArg !== thisArg
        );
    });
};

/**
 * Fires an event to listeners.
 * @param {object} pageRef - Reference of the page that represents the event scope.
 * @param {string} eventName - Name of the event to fire.
 * @param {*} payload - Payload of the event to fire.
 */
const fireEvent = (pageRef, eventName, payload) => {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            if (samePageRef(pageRef, listener.thisArg.pageRef)) {
                try {
                    listener.callback.call(listener.thisArg, payload);
                } catch (error) {
                    // fail silently
                }
            }
        });
    }
};

export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent
};
0
Subscribe to my newsletter

Read articles from Kapil Chauhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kapil Chauhan
Kapil Chauhan