我有一个使用useState
处于某种状态的组件。
此组件将打开一个新选项卡,并且应该在postMessage上收到消息事件。 由于某种原因,在我收到事件后,状态会返回到其初始状态。
我尝试将状态保存到localStorage并起作用。
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>
状态返回到其初始状态。
一个演示可以在这里看到: https://codesandbox.io/s/trusting-waterfall-9jzj7
顺便说一句:
我知道没有从tab.html
触发该事件,但是仍有一个事件在打开标签页时触发。
答案 0 :(得分:2)
似乎您的问题出在效果钩子的deps数组上,并且在每个渲染器上都创建了onMessageReceivedFromIframe
。
以下解决方案应能按预期工作。这是指向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>;
};
答案 1 :(得分:0)
问题出在下面的代码上
useEffect(() => {
addIframeListener();
return () => {
removeIframeListener();
};
}, []);
自从addIframeListener
内部调用removeIframeListener
和effect
以来,状态state
正在内部使用。
将代码更改为
useEffect(() => {
addIframeListener();
return () => {
removeIframeListener();
};
}, [state]);
如果framelistener
函数使用了任何道具,请将其保留在依赖数组中,否则最终会得到旧道具。