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
2 Answers
Reset to default 5The 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.