-
Notifications
You must be signed in to change notification settings - Fork 50.4k
Warn when passing invalid containers to render and unmountComponentAtNode #2065
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -370,8 +370,21 @@ var ReactMount = { | |
| var reactRootElement = getReactRootElementInContainer(container); | ||
| var containerHasReactMarkup = | ||
| reactRootElement && ReactMount.isRenderedByReact(reactRootElement); | ||
| var containerHasNonRootReactChild = | ||
| ReactMount.hasNonRootReactChild(container); | ||
|
|
||
| var shouldReuseMarkup = containerHasReactMarkup && !prevComponent; | ||
| if (__DEV__) { | ||
| warning( | ||
| !containerHasNonRootReactChild, | ||
| 'renderComponent(...): Replacing React-rendered children with a new ' + | ||
| 'root component.' | ||
| ); | ||
| } | ||
|
|
||
| var shouldReuseMarkup = | ||
| containerHasReactMarkup && | ||
| !prevComponent && | ||
| !containerHasNonRootReactChild; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one gets even more awkward. I tend to like a style here that we don't really do in FB, so here's what we should probably do: var shouldReuseMarkup =
containerHasReactMarkup &&
!prevComponent &&
!containerHasNonRootReactChild; |
||
|
|
||
| var component = ReactMount._renderNewRootComponent( | ||
| nextDescriptor, | ||
|
|
@@ -459,6 +472,28 @@ var ReactMount = { | |
| var reactRootID = getReactRootID(container); | ||
| var component = instancesByReactRootID[reactRootID]; | ||
| if (!component) { | ||
| // Check if the node being unmounted was rendered by React, but isn't a | ||
| // root node. | ||
| var containerHasNonRootReactChild = ReactMount.hasNonRootReactChild( | ||
| container); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style nit: this should be or (twice in this file) |
||
|
|
||
| // Check if the container itself is a React root node. | ||
| var containerID = ReactMount.getID(container); | ||
| var containerRootID = ReactInstanceHandles.getReactRootIDFromNodeID( | ||
| containerID); | ||
| var isContainerReactRoot = | ||
| containerID && containerRootID && containerID === containerRootID; | ||
|
|
||
| if (__DEV__) { | ||
| warning( | ||
| !containerHasNonRootReactChild, | ||
| 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + | ||
| 'is not a valid React root node, and thus cannot be unmounted.%s', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe "was rendered by React and cannot be unmounted using unmountComponentAtNode."? In the case that it's not a root, we could also add a "Instead, have the parent component update its state and rerender to remove this component." or similar. |
||
| (isContainerReactRoot ? ' You may have passed in a React root ' + | ||
| 'node as argument, rather than its container.' : '') | ||
| ); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| ReactMount.unmountComponentFromNode(component, container); | ||
|
|
@@ -560,6 +595,22 @@ var ReactMount = { | |
| return id ? id.charAt(0) === SEPARATOR : false; | ||
| }, | ||
|
|
||
| /** | ||
| * True if the supplied DOM node has a direct React-rendered child that is | ||
| * not a React root element. Useful for warning in renderComponent`, | ||
| * `unmountComponentAtNode`, etc. | ||
| * | ||
| * @param {?DOMElement} node The candidate DOM node. | ||
| * @return {boolean} True if the DOM element contains a direct child that was | ||
| * rendered by React but is not a root element. | ||
| * @internal | ||
| */ | ||
| hasNonRootReactChild: function(node) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function looks good, but can you move it to just be a free function at the top of this file? no reason it needs to be on the ReactMount object. |
||
| var reactRootID = getReactRootID(node); | ||
| return reactRootID ? reactRootID !== | ||
| ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false; | ||
| }, | ||
|
|
||
| /** | ||
| * Traverses up the ancestors of the supplied node to find a node that is a | ||
| * DOM representation of a React component. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a "If you intended to update the children of this node, you should instead have the component that rendered the existing children update its state and render the new components instead of calling the top-level React.render to update a subtree."
Also, s/renderComponent/render/. :)