最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - onClick not working inside the pop up opened via React portals - Stack Overflow

programmeradmin2浏览0评论

Below is the code that I have implemented:

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Link} from 'react-router'
import {Table, Column} from '../../Layout/ponents/Table'
import ReactDOM from 'react-dom'
// import ExternalPortal from '../../Sites/ponents/ExternalPortal'

export const TableName = 'ProjectDashboard'

class MyWindowPortal extends Component {
  static propTypes = {
    children: PropTypes.node,
    closeWindowPortal: PropTypes.func
  }

  constructor (props) {
    super(props)
    this.containerEl = document.createElement('div') // STEP 1: create an empty div
    this.externalWindow = null
  }

  ponentDidMount () {
    // STEP 3: open a new browser window and store a reference to it
    this.externalWindow = window.open('', '', 'width=600,height=400')

    // STEP 4: append the container <div> (that has props.children appended to it) to the body of the new window
    this.externalWindow.document.body.appendChild(this.containerEl)

    this.externalWindow.document.title = 'A React portal window'
    // copyStyles(document, this.externalWindow.document)

    // update the state in the parent ponent if the user closes the
    // new window
    this.externalWindow.addEventListener('beforeunload', () => {
      this.props.closeWindowPortal()
    })
  }

  ponentWillUnmount () {
    // This will fire when this.state.showWindowPortal in the parent ponent bees false
    // So we tidy up by just closing the window
    this.externalWindow.close()
  }

  render () {
    // STEP 2: append props.children to the container <div> that isn't mounted anywhere yet
    return ReactDOM.createPortal(this.props.children, this.containerEl)
  }
}


export default class ProjectTable extends Component {
  constructor (props) {
    super(props)

    this.state={showWindowPortal:false}

    this.toggleWindowPortal = this.toggleWindowPortal.bind(this)
    this.closeWindowPortal = this.closeWindowPortal.bind(this)
  }

  static propTypes = {
    data: PropTypes.object.isRequired,
    loginId: PropTypes.string.isRequired
  }

  ponentDidMount () {
    window.addEventListener('beforeunload', () => {
      this.closeWindowPortal()
    })
  }

  toggleWindowPortal () {
    this.setState({showWindowPortal: !this.state.showWindowPortal})
  }

  closeWindowPortal () {
    this.setState({showWindowPortal: false})
  }

  render () {
    return (
      <div>

        <div>
          <p>This div is just for testing click here to see the portal</p>
          {
            this.state.showWindowPortal &&
            (
              <MyWindowPortal closeWindowPortal={this.closeWindowPortal}>
                <button
                  onClick={() => this.closeWindowPortal()}
                >
                  Close
                </button>
              </MyWindowPortal>
            )
          }

          <button onClick={this.toggleWindowPortal}>
            {this.state.showWindowPortal ? 'Close the' : 'Open a'} Portal
          </button>
        </div>
      </div>
    )
  }
}

The button inside the portal doesn't trigger anything on click. It doesn't fire at all. Please note that MyWindowPortal opens a new window and the button gets rendered in that window. Tried testing it on Firefox and Chrome. I am not sure if I am doing anything wrong.

Below is the code that I have implemented:

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Link} from 'react-router'
import {Table, Column} from '../../Layout/ponents/Table'
import ReactDOM from 'react-dom'
// import ExternalPortal from '../../Sites/ponents/ExternalPortal'

export const TableName = 'ProjectDashboard'

class MyWindowPortal extends Component {
  static propTypes = {
    children: PropTypes.node,
    closeWindowPortal: PropTypes.func
  }

  constructor (props) {
    super(props)
    this.containerEl = document.createElement('div') // STEP 1: create an empty div
    this.externalWindow = null
  }

  ponentDidMount () {
    // STEP 3: open a new browser window and store a reference to it
    this.externalWindow = window.open('', '', 'width=600,height=400')

    // STEP 4: append the container <div> (that has props.children appended to it) to the body of the new window
    this.externalWindow.document.body.appendChild(this.containerEl)

    this.externalWindow.document.title = 'A React portal window'
    // copyStyles(document, this.externalWindow.document)

    // update the state in the parent ponent if the user closes the
    // new window
    this.externalWindow.addEventListener('beforeunload', () => {
      this.props.closeWindowPortal()
    })
  }

  ponentWillUnmount () {
    // This will fire when this.state.showWindowPortal in the parent ponent bees false
    // So we tidy up by just closing the window
    this.externalWindow.close()
  }

  render () {
    // STEP 2: append props.children to the container <div> that isn't mounted anywhere yet
    return ReactDOM.createPortal(this.props.children, this.containerEl)
  }
}


export default class ProjectTable extends Component {
  constructor (props) {
    super(props)

    this.state={showWindowPortal:false}

    this.toggleWindowPortal = this.toggleWindowPortal.bind(this)
    this.closeWindowPortal = this.closeWindowPortal.bind(this)
  }

  static propTypes = {
    data: PropTypes.object.isRequired,
    loginId: PropTypes.string.isRequired
  }

  ponentDidMount () {
    window.addEventListener('beforeunload', () => {
      this.closeWindowPortal()
    })
  }

  toggleWindowPortal () {
    this.setState({showWindowPortal: !this.state.showWindowPortal})
  }

  closeWindowPortal () {
    this.setState({showWindowPortal: false})
  }

  render () {
    return (
      <div>

        <div>
          <p>This div is just for testing click here to see the portal</p>
          {
            this.state.showWindowPortal &&
            (
              <MyWindowPortal closeWindowPortal={this.closeWindowPortal}>
                <button
                  onClick={() => this.closeWindowPortal()}
                >
                  Close
                </button>
              </MyWindowPortal>
            )
          }

          <button onClick={this.toggleWindowPortal}>
            {this.state.showWindowPortal ? 'Close the' : 'Open a'} Portal
          </button>
        </div>
      </div>
    )
  }
}

The button inside the portal doesn't trigger anything on click. It doesn't fire at all. Please note that MyWindowPortal opens a new window and the button gets rendered in that window. Tried testing it on Firefox and Chrome. I am not sure if I am doing anything wrong.

Share Improve this question asked Dec 20, 2017 at 15:36 shet_tayyyshet_tayyy 5,75512 gold badges54 silver badges93 bronze badges 4
  • You're opening in a new windows literally? Not just a div? An old school popup? If not check nothing is invisibly obscuring the button and that the callback is definitely not being called. – nanobar Commented Dec 20, 2017 at 15:46
  • @DominicTobias Yes, I am opening a new window and not just in the div. Yea, like an old-school pop up. The obscure thing came to my mind too. Trying to figure out how to find what's obscuring? Or maybe that's not an issue at all. Followed hackernoon./… – shet_tayyy Commented Dec 21, 2017 at 8:24
  • Sounds plex, you need to treat a new window just like a new web page that needs to load in react and your code and attach to a node etc – nanobar Commented Dec 21, 2017 at 9:56
  • I have the same problem as well. Any solutions ? – Nezih Commented Jun 28, 2018 at 8:09
Add a ment  | 

2 Answers 2

Reset to default 5

The solution has been posted here https://github./facebook/react/issues/12355

The issue is that you're first creating a DOM node in one document, but then moving it to another one. However React has already bound the event handlers to the first document. React doesn't expect that you would move a DOM node between documents while something is rendered into it

To fix it, you can change your code so that you move the node before rendering something into in React

class Window extends React.Component {
  constructor(props) {
    super(props);
    this.state = { win: null, el: null };
  }

  ponentDidMount() {
    let win = window.open('', '', 'width=600,height=400');
    win.document.title = 'A React portal window';
    let el = document.createElement('div');
    win.document.body.appendChild(el);
    this.setState({ win, el });
  }

  ponentWillUnmount() {
    this.state.win.close();
  }

  render() {
    const { el } = this.state;
    if (!el) {
      return null;
    }
    return ReactDOM.createPortal(this.props.children, el);
  }
}

I've faiced the same problems with onClick handlers but in my case I used ponent inside WindowPortal. My workaround for the problem was usage of ref's and manual assignment of onclick event handlers to each of button elements I had inside my ponent. Here is snippet example:

class SomeComponent extends Component {
  constructor(props) {
    super(props);

    this.ref = (element) => {
      element.querySelector('button.one').onclick = () => {
        alert( 'One!' );
      }
      element.querySelector('button.two').onclick = () => {
        alert( 'Two!' );
      }
    }
  }

  render() {
    return <div className="some-ponent" ref={this.ref}>
      <button type='button' className='one'>Click One</button>
      <button type='button' className='two'>Click Two</button>
    </div>;
  }
}

Btw, I'm using redux and SomeComponent successfully connects to store and allows dispatching of actions.

发布评论

评论列表(0)

  1. 暂无评论