我特别需要在浏览器中收听自定义事件,从那里,我有一个按钮可以打开一个弹出窗口。我目前正在使用 React Portal 打开另一个窗口 (PopupWindow),但是当我在其中使用挂钩时它不起作用 - 但如果我使用类则可以。我的意思是,当窗口打开时,两者都显示其下方的 div,但当事件数据刷新时,带有挂钩的那个会擦除它。要进行测试,请将窗口打开至少 5 秒钟。
我在 CodeSandbox 中有一个示例,但我也会在此处发布,以防网站出现故障或发生其他问题:
https://codesandbox.io/s/k20poxz2j7
下面的代码不会运行,因为我不知道如何通过 react cdn 使 react hooks 工作,但你现在可以用上面的链接测试它
const { useState, useEffect } = React;
function getRandom(min, max) {
const first = Math.ceil(min)
const last = Math.floor(max)
return Math.floor(Math.random() * (last - first + 1)) + first
}
function replaceWithRandom(someData) {
let newData = {}
for (let d in someData) {
newData[d] = getRandom(someData[d], someData[d] + 500)
}
return newData
}
const PopupWindowWithHooks = props => {
const containerEl = document.createElement('div')
let externalWindow = null
useEffect(
() => {
externalWindow = window.open(
'',
'',
`width=600,height=400,left=200,top=200`
)
externalWindow.document.body.appendChild(containerEl)
externalWindow.addEventListener('beforeunload', () => {
props.closePopupWindowWithHooks()
})
console.log('Created Popup Window')
return function cleanup() {
console.log('Cleaned up Popup Window')
externalWindow.close()
externalWindow = null
}
},
// Only re-renders this component if the variable changes
[]
)
return ReactDOM.createPortal(props.children, containerEl)
}
class PopupWindow extends React.Component {
containerEl = document.createElement('div')
externalWindow = null
componentDidMount() {
this.externalWindow = window.open(
'',
'',
`width=600,height=400,left=200,top=200`
)
this.externalWindow.document.body.appendChild(this.containerEl)
this.externalWindow.addEventListener('beforeunload', () => {
this.props.closePopupWindow()
})
console.log('Created Popup Window')
}
componentWillUnmount() {
console.log('Cleaned up Popup Window')
this.externalWindow.close()
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.containerEl
)
}
}
function App() {
let data = {
something: 600,
other: 200
}
let [dataState, setDataState] = useState(data)
useEffect(() => {
let interval = setInterval(() => {
setDataState(replaceWithRandom(dataState))
const event = new CustomEvent('onOverlayDataUpdate', {
detail: dataState
})
document.dispatchEvent(event)
}, 5000)
return function clear() {
clearInterval(interval)
}
}, [])
useEffect(
function getData() {
document.addEventListener('onOverlayDataUpdate', e => {
setDataState(e.detail)
})
return function cleanup() {
document.removeEventListener(
'onOverlayDataUpdate',
document
)
}
},
[dataState]
)
console.log(dataState)
// State handling
const [isPopupWindowOpen, setIsPopupWindowOpen] = useState(false)
const [
isPopupWindowWithHooksOpen,
setIsPopupWindowWithHooksOpen
] = useState(false)
const togglePopupWindow = () =>
setIsPopupWindowOpen(!isPopupWindowOpen)
const togglePopupWindowWithHooks = () =>
setIsPopupWindowWithHooksOpen(!isPopupWindowWithHooksOpen)
const closePopupWindow = () => setIsPopupWindowOpen(false)
const closePopupWindowWithHooks = () =>
setIsPopupWindowWithHooksOpen(false)
// Side Effect
useEffect(() =>
window.addEventListener('beforeunload', () => {
closePopupWindow()
closePopupWindowWithHooks()
})
)
return (
<div>
<button type="buton" onClick={togglePopupWindow}>
Toggle Window
</button>
<button type="buton" onClick={togglePopupWindowWithHooks}>
Toggle Window With Hooks
</button>
{isPopupWindowOpen && (
<PopupWindow closePopupWindow={closePopupWindow}>
<div>What is going on here?</div>
<div>I should be here always!</div>
</PopupWindow>
)}
{isPopupWindowWithHooksOpen && (
<PopupWindowWithHooks
closePopupWindowWithHooks={closePopupWindowWithHooks}
>
<div>What is going on here?</div>
<div>I should be here always!</div>
</PopupWindowWithHooks>
)}
</div>
)
}
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
<script crossorigin src="https://unpkg.com/react@16.7.0-alpha.2/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16.7.0-alpha.2/umd/react-dom.development.js"></script>
<div id="root"></div>
原文由 Rafael ‘BSIDES’ Pereira 发布,翻译遵循 CC BY-SA 4.0 许可协议
const [containerEl] = useState(document.createElement('div'));
编辑
按钮 onClick 事件,调用功能组件 PopupWindowWithHooks 的 第一次 调用,它按预期工作(创建新的
<div>
,在 useEffect 中附加<div>
到弹出窗口)。事件刷新, 再次 调用功能组件 PopupWindowWithHooks 和行
const containerEl = document.createElement('div')
create new<div>
。但是(第二个)新的<div>
永远不会附加到弹出窗口,因为行externalWindow.document.body.appendChild(containerEl)
在 useEffect 挂钩中,它只会在挂载时运行并在卸载时清理(第二个参数是空数组 [])。最后
return ReactDOM.createPortal(props.children, containerEl)
使用第二个参数 containerEl 创建门户 - 新的未附加<div>
使用 containerEl 作为有状态值(useState hook),问题就解决了:
编辑2
代码沙盒: https://codesandbox.io/s/l5j2zp89k9