Home > Mobile >  How to handle window event listeners in react
How to handle window event listeners in react

Time:10-04

In react i need to be able to open a popup window https://developer.mozilla.org/en-US/docs/Web/API/Window/open and manage the events such as "mesage" https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage and "load" and "close" events.

However none of the events i have added listeners to are firing...

import * as React from 'react';
import './style.css';
import { useState, useRef } from 'react';

export default function App() {
  const { login, error } = useOAuth();

  return (
    <div>
      <button onClick={login}>Login</button>
    </div>
  );
}

const useOAuth = () => {
  const [error, setError] = useState();
  const popupRef = useRef<Window | null | undefined>();

  const login = () => {
    popupRef.current = openPopup('https://google.com');
    popupRef.current.addEventListener('load', handlePopupLoad);
    popupRef.current.addEventListener('close', handlePopupClose);
    popupRef.current.addEventListener('message', handlePopupMessage);
  };

  const handlePopupLoad = (data) => {
    console.log('load', data);
  };

  const handlePopupClose = (data) => {
    console.log('close', data);
  };

  const handlePopupMessage = (data) => {
    console.log('message', data);
  };

  const openPopup = (url: string) => {
    const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
        width=500,height=600,left=100,top=100`;

    return window.open(url, 'Login', params);
  };

  return {
    login,
    error,
  };
};

https://stackblitz.com/edit/react-ts-qlfw9q?file=App.tsx

aside:

  1. Is there a way to differentiate between when a "user" closed the window using the "red x" button and when it was correctly closed using window.close().
  2. how can i nicely cleanup the popup once its closed.

CodePudding user response:

I think there is no react specific issue all you need to do to make this code work is to change your login function something like this.

const login = () => {
    const childWindow = openPopup('https://same-origin.com');
    childWindow.addEventListener('load', handlePopupLoad);
    childWindow.addEventListener('close', handlePopupClose);
    childWindow.addEventListener('message', handlePopupMessage);
  };

CodePudding user response:

well, you should probably wrap all your event listeners inside a useEffect to run it and cleanup after it, it should look like something like this

const popupRef = useRef<Window | null>(null)

    const handlePopupLoad = (data: any) => {
        console.log('load', data)
    }

    const handlePopupClose = (data: any) => {
        console.log('close', data)
    }

    const handlePopupMessage = (data: any) => {
        console.log('message', data)
    }

    const openPopup = (url: string) => {
        const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
        width=500,height=600,left=100,top=100`

        return window.open(url, 'Login', params)
    }

    useEffect(() => {
        if (!popupRef.current) {
            return undefined
        }

        popupRef.current = openPopup('https://google.com')
        popupRef.current?.addEventListener('load', handlePopupLoad)
        popupRef.current?.addEventListener('close', handlePopupClose)
        popupRef.current?.addEventListener('message', handlePopupMessage)

        return () => {
            popupRef.current?.removeEventListener('load', handlePopupLoad)
            popupRef.current?.removeEventListener('close', handlePopupClose)
            popupRef.current?.removeEventListener('message', handlePopupMessage)
        }
    }, [popupRef])

CodePudding user response:

I have changed the URL to a local one (to avoid any cross-origin issues).

Check out the demo (If this fails to load, try refreshing. Something seems to be off with Stackblitz)

In parent page, I have used the onload, onunload and

import React, { useRef } from 'react';

export default function App() {
  const popupRef = useRef<Window | null | undefined>();

  const login = () => {
    console.clear();

    const url = '/page2'; // 'https://google.com'
    popupRef.current = openPopup(url);

    if (popupRef.current === null) return;

    // When the popup loads
    popupRef.current.onload = () => {
      console.log('loaded. this was logged');
    };

    // when the popup unloads
    popupRef.current.onunload = () => {
      console.log('unloading now');
    };

    // when the popup posts a message
    popupRef.current.addEventListener('message', ({ data }) => {
      console.log('message: ', data);
    });
  };

  const openPopup = (url: string) => {
    const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
        width=500,height=600,left=100,top=100`;

    return window.open(url, 'Login', params);
  };

  return (
    <div>
      <button onClick={login}>Login</button>
    </div>
  );
}

And on the page2, I have added a button to trigger some message posting to parent.

export default function Page2() {
  const onClick = () => {
    window.parent.postMessage('To parent');
  };

  return (
    <div>
      <h1>This is page 2</h1>
      <button onClick={onClick}>Click me</button>
    </div>
  );
}

Few things I observed:

  • Since we are posting message from child to parent, I would expect window.addEventListener('message') to get triggered. But, for some reason, is popupRef.current.addEventListener('message') getting triggered.
  • popupRef.current.onunload gets triggered before the beginning of onload. If I had to guess, this is some sort of cleanup mechanism.
  • Related