'Firefox is terminating web socket connections when a mailto is triggered
I have a React application that establishes a web socket connection.
A part of my application is trying to open a mail client through mailto:, like this:
window.location.href = "mailto:[email protected]";
Unlike Chrome, Firefox is forcing websocket to close, every time an assignment to location.href occurs. To better illustrate the problem, here is a quick demo application.
import * as React from "react";
import "./styles.css";
export default function App() {
React.useEffect(() => {
console.log("component mount!");
return () => {
console.log("component unmounting");
};
}, []);
React.useEffect(() => {
const webSocket = new WebSocket("wss://echo.websocket.events/");
webSocket.onopen = (...args) => {
console.log("ws opened!", args);
};
webSocket.onerror = (err) => {
console.log("ws err received!", err);
};
webSocket.onclose = (err) => {
console.log("ws closing!", err);
};
return () => {
webSocket.close();
};
}, []);
const openInDocument = React.useCallback(() => {
window.location.href = "mailto:[email protected]";
}, []);
return (
<div className="App">
<button onClick={openInDocument}>Open mail client</button>
</div>
);
}
- Running the demo on Chrome will open your default mail client as expected.
- Firefox, will close the WS communication first, as seen on the console. Also note that the React component never gets unmounted!
I don't know if this is a bug on Firefox, (or is Chrome's laziness to destroy the ws connection).
I came up with 2 different solutions, but none of them feels correct to me:
- Open the mailto: link on a new tab, and close it afterwards.
Cons:
- Popup blocker may invoke and block the mailto:
- User will see a flash on her screen.
- Open the mailto: link on an iframe. Solution seems legit but it seems to me a bit dirty, plus I don't know if future browser versions will allow that behaviour.
Here is the sample code
import * as React from "react";
import "./styles.css";
export default function App() {
const iframeRef = React.useRef<HTMLIFrameElement>(null);
React.useEffect(() => {
console.log("component mount!");
return () => {
console.log("component unmounting");
};
}, []);
React.useEffect(() => {
const webSocket = new WebSocket("wss://echo.websocket.events/");
webSocket.onopen = (...args) => {
console.log("ws opened!", args);
};
webSocket.onerror = (err) => {
console.log("ws err received!", err);
};
webSocket.onclose = (err) => {
console.log("ws closing!", err);
};
return () => {
webSocket.close();
};
}, []);
const openInDocument = React.useCallback(() => {
window.location.href = "mailto:[email protected]";
}, []);
const openInNewWindow = React.useCallback(() => {
const win = window.open("mailto:[email protected]");
win?.close();
}, []);
const openThroughIframe = React.useCallback(() => {
iframeRef.current?.setAttribute("src", "mailto:[email protected]");
}, []);
return (
<div className="App">
<iframe
title="mailto opener"
ref={iframeRef}
style={{ width: 0, height: 0, border: 0 }}
></iframe>
<button onClick={openInDocument}>Open mail client</button>
<br />
<button onClick={openInNewWindow}>
Open mail client using new tab/window
</button>
<br />
<button onClick={openThroughIframe}>Open through iframe</button>
</div>
);
}
and a codesandbox link
Can you suggest any better solution? Should I proceed with the iframe solution?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
