4

I am building a LWC with the intent to listen to an event from window.postMessage via window.addEventListner. However, after deploying the below code, I am not seeing any console logs from my LWC when I use chrome console to post the message to the window. I have tried using both this.addEventListener and window.addEventListener.

This is my first time building a LWC so I presume I am doing something wrong.

import { LightningElement } from 'lwc';

export default class MyEventHandler extends LightningElement {
    constructor() {
        super();
        this.addEventListener('message', this.handleMessage);
      }
      handleMessage = (event) => {
          console.log('event received')
          console.log(event);
      };
}

1 Answer 1

7

You definitely need to use window.addEventListener and window.removeEventListener. I also recommend using connectedCallback instead of constructor (though it should work either way).

import { LightningElement } from "lwc";

export default class App extends LightningElement {
  message = 'none received (yet)'
  connectedCallback() {
    window.addEventListener('message', this.receiveMessage);
  }
  disconnectedCallback() {
    window.removeEventListener('message', this.receiveMessage);
  }
  receiveMessage = (event) => {
    this.message = event.data
  }
}

import { LightningElement, api } from "lwc";

export default class Child extends LightningElement {
  sendMessage() {
    window.postMessage('hello world', '*');
  }
}

Note that arrow functions keep their this reference to the class. If you use a normal function, you instead must use bind.

  #receiveMessageCallback;
  connectedCallback() {
    this.#receiveMessageCallback = this.receiveMessage.bind(this);
    window.addEventListener('message', this.#receiveMessageCallback);
  }
  disconnectedCallback() {
    window.removeEventListener('message', this.#receiveMessageCallback);
  }

We need to store the original event handler (#receiveMessageCallback), otherwise we will leak memory when the component unloads.

Demo.

8
  • Thanks! That worked! Aug 29, 2021 at 15:49
  • 1
    Are you sure this is correct? AFAIK you lose the "this" binding for the component when adding the event listener this way (against window rather than this or this.template).
    – Phil W
    Feb 16, 2023 at 13:41
  • @PhilW I should have explained better, I suppose. Unlike normal function objects, Arrow functions (() => {}) keep their this to whatever scope they are defined in. receiveMessage(event) wouldn't work here, but receiveMessage = (event) => {...} does.
    – sfdcfox
    Feb 16, 2023 at 14:18
  • 1
    A-ha, yes, I should have read the whole piece of code and spotted the arrow function for this.receiveMessage. My fault for not being thorough, but it's a good idea to add a bit of detail in the answer too :D
    – Phil W
    Feb 16, 2023 at 14:22
  • 1
    @PhilW It's the new private class fields. This makes it explicit that the property is not to be accessed outside the class.
    – sfdcfox
    Feb 16, 2023 at 14:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .