Reputation: 239
I have a component with some state using useState
.
This component opens a new tab and should receive a message event on postMessage. For some reason, after I get the event, the state returns to its initial state.
I tried saving the state to localStorage and it worked.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [state, setState] = useState("hello");
const onClick = () => {
setState("world");
window.open("tab.html");
};
const onMessageReceivedFromIframe = event => {
console.log("onMessageReceivedFromIframe", state, event);
};
const addIframeListener = () =>
window.addEventListener("message", onMessageReceivedFromIframe);
const removeIframeListener = () =>
window.removeEventListener("message", onMessageReceivedFromIframe);
useEffect(() => {
addIframeListener();
return () => {
removeIframeListener();
};
}, []);
useEffect(() => {
console.log("Current state: ", state);
}, [state]);
return <button onClick={onClick}>Click here</button>;
};
ReactDOM.render(<App />, document.getElementById("app"));
<html>
<body>
tab
<script>
setTimeout(() => {
console.log("post");
window.opener.postMessage("some message", "https://9jzj7.csb.app");
window.close();
}, 2000);
</script>
</body>
</html>
The state returns to its initial state.
A demo can be seen here: https://codesandbox.io/s/trusting-waterfall-9jzj7
BTW:
I know that the event is not fired from tab.html
but there is still an event that fires on tab opening.
Upvotes: 11
Views: 9957
Reputation: 1596
The problem is with the below code
useEffect(() => {
addIframeListener();
return () => {
removeIframeListener();
};
}, []);
Since its calling addIframeListener
and removeIframeListener
from inside effect
and the state state
is getting used inside it.
Change the code as
useEffect(() => {
addIframeListener();
return () => {
removeIframeListener();
};
}, [state]);
If there is any props getting used by framelistener
function, keep that in dependent array otherwise you will end up getting old props
Upvotes: 1
Reputation: 5226
seems your problem was with the deps array of your effect hook, and onMessageReceivedFromIframe
being created on every render.
the solution below should work like you expect. here is a link to the codesandbox
const App = () => {
const [state, setState] = useState("hello");
const onClick = () => {
setState("world");
console.log(state);
window.open("tab.html");
};
const onMessageReceivedFromIframe = React.useCallback(
event => {
console.log("onMessageReceivedFromIframe", state, event);
},
[state]
);
useEffect(() => {
window.addEventListener("message", onMessageReceivedFromIframe);
return () =>
window.removeEventListener("message", onMessageReceivedFromIframe);
}, [onMessageReceivedFromIframe]);
React.useEffect(() => {
console.log("state", state);
}, [state]);
return <button onClick={onClick}>Click here</button>;
};
Upvotes: 5