Skip to main content
added 523 characters in body
Source Link
connectedCallback() {
    window.addEventListener("message", this.myMessageHandler, false);
}
//Local Var
myMessageHandler = this.onMessage.bind(this);
onMessage(e){
    try {
        if (e.data) {
            //Event received
            console.log('Post Message myMessageHandler Received with data', JSON.stringify(e.data));
        }else{
            console.log('Post Message myMessageHandler Received, no data')
        }

    } catch (e) {
        console.log(e);
    }
}

disconnectedCallback(){
    window.addEventListenerremoveEventListener("message", this.myMessageHandler, false);
}

and on component disconnect call backThis way, we will runcan remove the listener by implementing

window.removeEventListener("message", this.myMessageHandler)

in the disconnectedCallback.

this.myMessageHandler = this.onMessage.bind(this)
window.addEventListener("message", this.myMessageHandler, false);

and on component disconnect call back, we will run

window.removeEventListener("message", this.myMessageHandler)

connectedCallback() {
    window.addEventListener("message", this.myMessageHandler, false);
}
//Local Var
myMessageHandler = this.onMessage.bind(this);
onMessage(e){
    try {
        if (e.data) {
            //Event received
            console.log('Post Message myMessageHandler Received with data', JSON.stringify(e.data));
        }else{
            console.log('Post Message myMessageHandler Received, no data')
        }

    } catch (e) {
        console.log(e);
    }
}

disconnectedCallback(){
    window.removeEventListener("message", this.myMessageHandler);
}

This way, we can remove the listener by implementing

window.removeEventListener("message", this.myMessageHandler)

in the disconnectedCallback.

Source Link

Using window.postMessage in Lightning Web Components and .bind(this)

We are trying to build a drag and drop interface where after a drop, we want to communicate up the component tree that an element has been dropped. We need to communicate to the original LWC component in order to do some post-drop processing.

The component structure currently is

  1. Level 0 - Builder
  2. Component A 1 - Level 1 - Components
  3. Component A 2 - Level 1.1 - Fields (draggable element)
  4. Component B 1 - Level 1 - Canvas
  5. Component B 2 - Level 1 - Page
  6. Component B 3 - Level 1.1 Grid Item
  7. Component B 4 - Level 1.2 Element (drop section)

The problem is communicating from B4 Canvas Element back to Level 0 - Builder and up again to A2 Fields.

We think of 2 ways of handling this -

  • 1 - build a communication bus where when Level 5 Canvas Element registers a drop, it communicates with new CustomEvent() back to the parent component, which in terms catches the event and re-emits another new CustomEvent() to it's parent and so on down to Level 1 Builder and use Parent to Child communication up to A2 Fields. This approach seems to work but is "ugly" and requires that all components in the tree implement the "catch event - emit event" function

  • 2 - using window.postMessage() when B4 Canvas Element registers a drop and then registering a listener on the components that need to monitor for drop through window.addEventListener("message", this.onMessage.bind(this), false); - notice, we require bind(this) in order to access local vars in the component catching the window.postMessage()

We favor the second approach as it's cleaner and doesn't require implementing an event bus on each component to communicate the drop event, however Salesforce advise against doing .bind(this) (https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_dom) with the warning:

Don’t use addEventListener(eventName, this.handleNotification.bind(this)). It’s an anti-pattern because bind() returns a new function and thus the component can’t call removeEventListener() with the same function instance. Because the component can’t use the same function instance, the listener leaks.

We think of getting around this by keeping the this.onMessage.bind(this) function in a local variable like

this.myMessageHandler = this.onMessage.bind(this)
window.addEventListener("message", this.myMessageHandler, false);

and on component disconnect call back, we will run

window.removeEventListener("message", this.myMessageHandler)

My concern is with Salesforce are telling us to not do .bind(this) but they are not giving us any alternatives.

What is the best way to handle the given scenario that is "clean" by salesforce standards? Any issues with our workaround of putting the event listener in a local variable? What is the best approach to communicate such an event between sibling components or components, multiple levels deep?