facebook / react

The library for web and native user interfaces.

Home Page:https://react.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: Wrong warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.

amorphousDj opened this issue · comments

commented

React version: 17.0.2

Steps To Reproduce

  1. click show button
  2. 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.”
image

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.

commented

Have you tried updating React?

I need more info
What does another copy of react mean ?

commented

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.

commented

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!