Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
274 views
in Technique[技术] by (71.8m points)

javascript - 如何使React Portal与React Hook一起使用?(How can I make React Portal work with React Hook?)

I have this specific need to listen to a custom event in the browser and from there, I have a button that will open a popup window.

(我有这个特殊的需要,要在浏览器中监听自定义事件,然后从那里,我有一个可以打开弹出窗口的按钮。)

I'm currently using React Portal to open this other window (PopupWindow), but when I use hooks inside it doesn't work - but works if I use classes.

(我目前正在使用React Portal打开另一个窗口(PopupWindow),但是当我在其中使用钩子时不起作用-但如果我使用类则可以。)

By working I mean, when the window opens, both shows the div below it but the one with hooks erases it when the data from the event refreshes.

(通过工作,我的意思是,当窗口打开时,两个窗口都显示其下方的div,但是当事件数据刷新时,带有钩子的窗口将擦除该div。)

To test, leave the window open for at least 5 seconds.

(要进行测试,请将窗口打开至少5秒钟。)

I have an example in a CodeSandbox, but I'm also post here in case the website is down or something:

(我在CodeSandbox中有一个示例,但如果网站关闭或其他原因,我也会在此处发布:)

https://codesandbox.io/s/k20poxz2j7

(https://codesandbox.io/s/k20poxz2j7)

The code below won't run because I don't know how to make react hooks work via react cdn but you can test it with the link above by now

(下面的代码无法运行,因为我不知道如何通过react cdn使react挂钩起作用,但是您现在可以使用上面的链接对其进行测试)

 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/[email protected]/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="root"></div> 

  ask by bsides translate from so


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

const [containerEl] = useState(document.createElement('div'));

EDIT

(编辑)

Button onClick event, invoke first call of functional component PopupWindowWithHooks and it works as expected (create new <div> , in useEffect append <div> to popup window).

(Button onClick事件,调用功能组件PopupWindowWithHooks的 首次调用,它按预期方式工作(在useEffect中创建新的<div> ,将<div>追加到弹出窗口)。)

The event refresh, invoke second call of functional component PopupWindowWithHooks and line const containerEl = document.createElement('div') create new <div> again.

(事件刷新后,调用功能组件PopupWindowWithHooks的 第二次调用,并在const containerEl = document.createElement('div')再次创建新的<div> 。)

But that (second) new <div> will never be appended to popup window, because line externalWindow.document.body.appendChild(containerEl) is in useEffect hook that would run only on mount and clean up on unmount (the second argument is an empty array []).

(但是,(第二个)新的<div>将永远不会追加到弹出窗口,因为在代码中使用了externalWindow.document.body.appendChild(containerEl)行,该钩子仅在mount上运行,而在unmount上清除(第二个参数是空数组[])。)

Finally return ReactDOM.createPortal(props.children, containerEl) create portal with second argument containerEl - new unappended <div>

(最后return ReactDOM.createPortal(props.children, containerEl)创建带有第二个参数containerEl的门户-新的未附加<div>)

With containerEl as a stateful value (useState hook), problem is solved:

(使用containerEl作为有状态值(useState挂钩),可以解决以下问题:)

const [containerEl] = useState(document.createElement('div'));

EDIT2

(编辑2)

Code Sandbox: https://codesandbox.io/s/l5j2zp89k9

(代码沙箱: https//codesandbox.io/s/l5j2zp89k9)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...