Bug: Wrong warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.
amorphousDj opened this issue · comments
React version: 17.0.2
Steps To Reproduce
- click show button
- click close button
Link to code example:
https://codesandbox.io/s/nodewarning-9fu7n
The current behavior
The console will print “Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.”
The expected behavior
The console will not print this warning.
I studied the test case for this warning. The Dialog component was rendered and unmounted by the same ReactDOM. This is a false warning.
Have you tried updating React?
I need more info
What does another copy of react mean ?
This seems to happen because the portal is not unmounted before you unmountComponentAtNode. I don't know enough about portals to say whether this is a bug, or whether there's either an idiomatic solution or workaround, but wanted to comment to confirm that this seems weird.
I think the problem is that ReactDOM.render
handles the container
but then your Dialog
manually inserts element into this container
via appendChild
. I don't think that's generally supported.
And once you call ReactDOM.unmountComponentAtNode(container)
React tries to unmount something that was was rendered via ReactDOM.createPortal
. So it may just be a misleading warning since it's not another copy of React but rather a node that can't be unmounted via unmountComponentAtNode
.
What's odd is that your specific example doesn't even need createPortal
. Dialog
could just as well be implemented as
render() {
return (
<div>
<div>
dialog
<button onClick={closeDialog}>close</button>
</div>
</div>
);
}
Your implementation creates the outer div
with DOM API + createPortal
.
The warning is issued because we call getInstanceFromNode(container.firstChild)
(remember that container.firstChild
was the target of createPortal
). getInstanceFromNode
returns null
for nodes that were the target of createPortal
. That's why we assume it's a different copy of React. But it's not really a different copy but a node that we didn't expect there.
What we could do in unmountComponentAtNode
is to check if that element is handled by this copy of React by checking all of the internal*Key
properties:
-const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
+const isUnmountable = rootEl && !getInstanceFromNode(rootEl);
+const renderedByThisReact = rootEl && (
+ (node: any)[internalInstanceKey] ||
+ (node: any)[internalContainerInstanceKey] ||
+ (node: any)[internalEventHandlersKey] ||
+ (node: any)[internalEventHandlerListenersKey] ||
+ (node: any)[internalEventHandlesSetKey]
+)
and then adjust the warning. But this is a lot of work for this specific case.
To further elaborate: The same warning appears with
const container = document.createElement("div");
document.body.appendChild(container);
class Dialog extends React.Component {
constructor(props) {
super(props);
const dom = document.createElement("div");
this.dom = dom;
}
componentDidMount() {
container.appendChild(this.dom);
}
componentWillUnmount() {
container.removeChild(this.dom);
}
render() {
return null
}
}
ReactDOM.render(<Dialog />, container);
ReactDOM.unmountComponentAtNode(container);
I don't think it's worth it to explain every single possible scenario if almost all issues are caused by a different copy of React.
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!