如何让 React Portal 与 React Hook 一起工作? [英] How can I make React Portal work with React Hook?

查看:13
本文介绍了如何让 React Portal 与 React Hook 一起工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我特别需要在浏览器中监听自定义事件,然后我有一个按钮可以打开一个弹出窗口.我目前正在使用 React Portal 打开另一个窗口(PopupWindow),但是当我在里面使用钩子时它不起作用 - 但如果我使用类就可以工作.通过工作,我的意思是,当窗口打开时,两者都显示其下方的 div,但带有钩子的 div 在事件数据刷新时将其擦除.要进行测试,请将窗口打开至少 5 秒钟.

我在 CodeSandbox 中有一个示例,但我也会在这里发布以防网站出现故障或其他原因:

https://codesandbox.io/s/k20poxz2j7

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

const { useState, useEffect } = React;函数 getRandom(min, max) {const first = Math.ceil(min)const last = Math.floor(max)return Math.floor(Math.random() * (last - first + 1)) + first}函数 replaceWithRandom(someData) {让新数据 = {}for(让 d in someData){newData[d] = getRandom(someData[d], someData[d] + 500)}返回新数据}const PopupWindowWithHooks = props =>{const containerEl = document.createElement('div')让 externalWindow = null使用效果(() =>{externalWindow = window.open('','',`宽=600,高=400,左=200,上=200`)externalWindow.document.body.appendChild(containerEl)externalWindow.addEventListener('beforeunload', () => {props.closePopupWindowWithHooks()})console.log('创建弹出窗口')返回函数清理(){console.log('清理弹出窗口')externalWindow.close()外部窗口 = 空}},//只有在变量改变时才重新渲染这个组件[])返回 ReactDOM.createPortal(props.children, containerEl)}类 PopupWindow 扩展了 React.Component {containerEl = document.createElement('div')外部窗口 = 空componentDidMount() {this.externalWindow = window.open('','',`宽=600,高=400,左=200,上=200`)this.externalWindow.document.body.appendChild(this.containerEl)this.externalWindow.addEventListener('beforeunload', () => {this.props.closePopupWindow()})console.log('创建弹出窗口')}componentWillUnmount() {console.log('清理弹出窗口')this.externalWindow.close()}使成为() {返回 ReactDOM.createPortal(this.props.children,this.containerEl)}}功能应用(){让数据 = {东西:600,其他:200}让 [dataState, setDataState] = useState(data)useEffect(() => {让间隔 = setInterval(() => {setDataState(replaceWithRandom(dataState))const event = new CustomEvent('onOverlayDataUpdate', {详细信息:数据状态})document.dispatchEvent(事件)}, 5000)返回函数清除(){清除间隔(间隔)}}, [])使用效果(函数 getData() {document.addEventListener('onOverlayDataUpdate', e => {setDataState(e.detail)})返回函数清理(){document.removeEventListener('onOverlayDataUpdate',文档)}},[数据状态])控制台日志(数据状态)//状态处理const [isPopupWindowOpen, setIsPopupWindowOpen] = useState(false)常量 [isPopupWindowWithHooksOpen,setIsPopupWindowWithHooksOpen] = 使用状态(假)const togglePopupWindow = () =>setIsPopupWindowOpen(!isPopupWindowOpen)const togglePopupWindowWithHooks = () =>setIsPopupWindowWithHooksOpen(!isPopupWindowWithHooksOpen)const closePopupWindow = () =>setIsPopupWindowOpen(false)const closePopupWindowWithHooks = () =>setIsPopupWindowWithHooksOpen(false)//副作用useEffect(() =>window.addEventListener('beforeunload', () => {关闭弹出窗口()closePopupWindowWithHooks()}))返回 (<div><button type="buton" onClick={togglePopupWindow}>切换窗口<button type="buton" onClick={togglePopupWindowWithHooks}>带钩子的切换窗口{isPopupWindowOpen &&(<PopupWindow closePopupWindow={closePopupWindow}><div>这是怎么回事?</div><div>我应该一直在这里!</div></PopupWindow>)}{isPopupWindowWithHooksOpen &&(<带钩子的弹出窗口closePopupWindowWithHooks={closePopupWindowWithHooks}><div>这是怎么回事?</div><div>我应该一直在这里!</div></PopupWindowWithHooks>)}

)}const rootElement = document.getElementById('root')ReactDOM.render(, 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>

解决方案

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

编辑

Button onClick 事件,调用功能组件 PopupWindowWithHooks第一次调用,它按预期工作(在 useEffect 中创建新的

附加到弹出窗口).

事件刷新,调用功能组件PopupWindowWithHooks第二次调用和const containerEl = document.createElement('div')行创建新

再次.但是那个(第二个)新的

永远不会被附加到弹出窗口,因为行 externalWindow.document.body.appendChild(containerEl) 在 useEffect 钩子中仅在安装时运行并在卸载时清理(第二个参数是一个空数组 []).

最后 return ReactDOM.createPortal(props.children, containerEl) 使用第二个参数创建门户 containerEl - 新的未附加的

containerEl 作为有状态的值(useState hook),问题解决了:

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

EDIT2

代码沙盒:https://codesandbox.io/s/l5j2zp89k9

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. 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. To test, leave the window open for at least 5 seconds.

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

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

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>

解决方案

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).

The event refresh, invoke second call of functional component PopupWindowWithHooks and line const containerEl = document.createElement('div') create new <div> again. 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 []).

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

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

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

EDIT2

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

这篇关于如何让 React Portal 与 React Hook 一起工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆