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:
- 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().
- 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, ispopupRef.current.addEventListener('message')
getting triggered. popupRef.current.onunload
gets triggered before the beginning ofonload
. If I had to guess, this is some sort of cleanup mechanism.