-
Notifications
You must be signed in to change notification settings - Fork 47.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Possible bug in ReactDOM.createPortal when used in a new window #12355
Comments
Also weird is this form of event handler doesn't work within the portal in a new window: class MyComponent extends React.Component {
constructor(props) {
super(props);
this.onButtonClicked = this.onButtonClicked.bind(this);
}
render() {
<button onClick={this.onButtonClicked}>Click me</button>;
}
} but this does: class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
<button onClick={e => this.onButtonClicked()}>Click me</button>;
}
} |
Also appears in Chrome and Firefox on Windows. |
This happens because if you add the DOM node to the new window in |
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. I wouldn't say it's a bug — React just can't detect this. To fix it, you can change your code so that you move the node before rendering something into in React. Like here: https://codepen.io/gaearon/pen/mjGvRV?editors=0010 class Window extends React.Component {
constructor(props) {
super(props);
this.state = { win: null, el: null };
}
componentDidMount() {
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 });
}
componentWillUnmount() {
this.state.win.close();
}
render() {
const { el } = this.state;
if (!el) {
return null;
}
return ReactDOM.createPortal(this.props.children, el);
}
} |
@gaearon I wish there was a solution to share/move a mounted node between documents and persist/rebind bindings. For example, when creating a popout in a new window... WindowA boots up, renders content in a container => That container contains a button to // in WindowA (root window)
class SomePage extends React.Component {
constructor(props) {
super(props)
this.contentRef = document.createElement('div')
}
render() {
/* elsewhere we have some logic to toggle/invoke the changing of whether the component should or should not be popped out */
const { isPoppedOut } = this.props
return (
<>
{/* Body appends the contentRef as a child */}
{!isPoppedOut && <Body contentRef={contentRef} />}
{/* PopoutWindow creates a new window, appends the contentRef to the new window */}
{isPoppedOut && <PopoutWindow contentRef={contentRef} />}
{ReactDOM.createPortal(
this.props.children,
this.contentRef
)}
</>
)
}
} |
|
Hi @gaearon I have implemented something similar, but when I click the browser close button in the new window , "componentWillUnmount" is not being invoked which is why state is not changing even when I close the portal using the browser close button. Is there any solution to this problem? |
thats because of ES6 I think. |
@gaearon I've used your codepen as a base for my test with "mousedown", "mousemove" and "mouseup" events. But the "mousemove" events are still being triggered by moving over the parent window, and not when moving over the pop out window. To reproduce: open the example, click on "Open a Portal", arrange both windows side by side, so you can see both and move the mouse between them. Click in the red div in the portal window, then click inside the white background of the parent window and move the mouse. The counter in the red div will update when moving inside the parent window, even though it was supposed to update only when 'dragging' the red div. How can I ensure that |
This solution has some problem when we need to copy styles from parent window to new window in ComponentDidMount. The UI gets distorted when new window is opened after adding this solution. `componentDidMount() { function copyStyles (sourceDoc, targetDoc) { |
Hi @sunilsp77, this is how I managed to copy the styles correctly, I feel like defering the styles-copy process to the next state update. const WindowPortal = ({ children }) => {
const [externalWindow, setExternalWindow] = useState(null);
const [containerRef, setContainerRef] = useState(null);
useEffect(() => {
const containerElement = document.createElement('div');
const externalWindow = window.open(
'',
'',
'width=600,height=400,left=200,top=200'
);
// append the element to the external document before setting ref
// so that React could detect event bindding correctly
// https://github.com/facebook/react/issues/12355
externalWindow.document.body.appendChild(containerElement);
setContainerRef(containerElement);
setExternalWindow(externalWindow);
return () => {
externalWindow?.close();
};
}, []);
useEffect(() => {
if (externalWindow) {
copyStyles(document, externalWindow.document);
}
}, [externalWindow]);
return containerRef && ReactDOM.createPortal(children, containerRef);
}; |
Do you want to request a feature or report a bug?
A bug.
What is the current behavior?
When a
ReactDOM.createPortal
is used in conjunction with a container in another window, the components do not respond to user input until aftersetState
orforceUpdate
are called on the parent component of the portal.I've produced a CodePen demonstrating the issue.
setState({})
on the parent).What is the expected behavior?
I expect the components in the new window to be interactive.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.2.0
ReactDOM 16.2.0
I've tested this in Safari and Chrome on Mac.
The text was updated successfully, but these errors were encountered: