Skip to content
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

Warn when mixing createRoot() and old APIs #14615

Merged
merged 7 commits into from
Jan 18, 2019

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Jan 17, 2019

It's confusing when you mix those — e.g. if you render with createRoot().render() but then call unmountComponentAtNode(). Sometimes it warns with misleading warnings, and sometimes (in particular, if there's existing content) it didn't warn at all. We don't have guarantees around how it works either (e.g. unmounting doesn't work).

I added an explicit DEV-only flag for whether something is a new-style root, and I warn whenever you try to mix the API one way or the other. The message suggests the right API to use. I didn't change the behavior.

I didn't clean up false positive further warnings because the code for them is all over the place and it's hard to untangle the assumptions. So warning at entry point seemed like the easiest thing to do. We want to keep these worlds separate anyway.

This way further warning check doesn't crash on bad inputs.
@sizebot
Copy link

sizebot commented Jan 17, 2019

ReactDOM: size: 0.0%, gzip: 🔺+0.1%

Details of bundled changes.

Comparing: 4feab7f...4253819

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js +0.2% +0.1% 730.47 KB 732.01 KB 168.74 KB 168.88 KB UMD_DEV
react-dom.production.min.js 0.0% 🔺+0.1% 98.88 KB 98.92 KB 32.2 KB 32.21 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 101.86 KB 101.9 KB 32.84 KB 32.84 KB UMD_PROFILING
react-dom.development.js +0.2% +0.1% 725.53 KB 727.07 KB 167.31 KB 167.46 KB NODE_DEV
react-dom.production.min.js 0.0% 0.0% 98.88 KB 98.92 KB 31.67 KB 31.67 KB NODE_PROD
react-dom.profiling.min.js 0.0% -0.0% 101.98 KB 102.02 KB 32.29 KB 32.29 KB NODE_PROFILING
ReactDOM-dev.js +0.3% +0.1% 747.08 KB 749.04 KB 168.4 KB 168.59 KB FB_WWW_DEV
ReactDOM-prod.js 0.0% 0.0% 310.8 KB 310.94 KB 57.42 KB 57.43 KB FB_WWW_PROD
ReactDOM-profiling.js 0.0% 0.0% 317.96 KB 318.11 KB 58.75 KB 58.76 KB FB_WWW_PROFILING
react-dom-unstable-fire.development.js +0.2% +0.1% 730.76 KB 732.36 KB 168.85 KB 169.02 KB UMD_DEV
react-dom-unstable-fire.production.min.js 0.0% 🔺+0.1% 98.89 KB 98.93 KB 32.21 KB 32.22 KB UMD_PROD
react-dom-unstable-fire.profiling.min.js 0.0% 0.0% 101.88 KB 101.92 KB 32.84 KB 32.85 KB UMD_PROFILING
react-dom-unstable-fire.development.js +0.2% +0.1% 725.82 KB 727.41 KB 167.42 KB 167.6 KB NODE_DEV
react-dom-unstable-fire.production.min.js 0.0% 0.0% 98.89 KB 98.94 KB 31.67 KB 31.68 KB NODE_PROD
react-dom-unstable-fire.profiling.min.js 0.0% -0.0% 102 KB 102.04 KB 32.3 KB 32.3 KB NODE_PROFILING
ReactFire-dev.js +0.3% +0.1% 746.23 KB 748.25 KB 168.32 KB 168.54 KB FB_WWW_DEV
ReactFire-prod.js 0.0% 0.0% 299.39 KB 299.54 KB 55.15 KB 55.16 KB FB_WWW_PROD
ReactFire-profiling.js 0.0% 0.0% 306.48 KB 306.63 KB 56.43 KB 56.43 KB FB_WWW_PROFILING
react-dom-test-utils.development.js 0.0% 0.0% 44.87 KB 44.87 KB 12.3 KB 12.3 KB UMD_DEV
react-dom-test-utils.production.min.js 0.0% 🔺+0.1% 9.97 KB 9.97 KB 3.71 KB 3.71 KB UMD_PROD
react-dom-test-utils.development.js 0.0% 0.0% 44.59 KB 44.59 KB 12.24 KB 12.24 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 🔺+0.1% 9.74 KB 9.74 KB 3.64 KB 3.65 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 60.61 KB 60.61 KB 15.92 KB 15.92 KB UMD_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 🔺+0.1% 11.01 KB 11.01 KB 3.81 KB 3.81 KB UMD_PROD
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 60.29 KB 60.29 KB 15.79 KB 15.79 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 🔺+0.1% 10.75 KB 10.75 KB 3.71 KB 3.71 KB NODE_PROD
react-dom-server.browser.development.js 0.0% 0.0% 124.7 KB 124.7 KB 33.28 KB 33.29 KB UMD_DEV
react-dom-server.browser.production.min.js 0.0% 0.0% 16.96 KB 16.96 KB 6.53 KB 6.53 KB UMD_PROD
react-dom-server.browser.development.js 0.0% 0.0% 120.83 KB 120.83 KB 32.36 KB 32.36 KB NODE_DEV
react-dom-server.browser.production.min.js 0.0% 0.0% 16.87 KB 16.87 KB 6.51 KB 6.51 KB NODE_PROD
ReactDOMServer-dev.js 0.0% 0.0% 122.02 KB 122.02 KB 32 KB 32 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% 0.0% 44.72 KB 44.72 KB 10.33 KB 10.33 KB FB_WWW_PROD
react-dom-server.node.development.js 0.0% 0.0% 122.89 KB 122.89 KB 32.9 KB 32.9 KB NODE_DEV
react-dom-server.node.production.min.js 0.0% 0.0% 17.74 KB 17.74 KB 6.83 KB 6.83 KB NODE_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 3.63 KB 3.63 KB 1.44 KB 1.44 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.4% 1.21 KB 1.21 KB 705 B 708 B UMD_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 3.45 KB 3.45 KB 1.39 KB 1.39 KB NODE_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.5% 1.05 KB 1.05 KB 636 B 639 B NODE_PROD
react-dom-unstable-fizz.node.development.js 0.0% +0.2% 3.7 KB 3.7 KB 1.42 KB 1.42 KB NODE_DEV
react-dom-unstable-fizz.node.production.min.js 0.0% 🔺+0.5% 1.1 KB 1.1 KB 666 B 669 B NODE_PROD

Generated by 🚫 dangerJS

warningWithoutStack(
!container._reactIsNewStyleRootDEV,
'You are calling ReactDOM.hydrate() on a container that was previously ' +
'managed by ReactDOM.%s(). This is not supported. ' +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created by

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"created by createRoot" sounded a bit tautological but I don't know if manage is better.

Container also wasn't technically "created" by createRoot. createRoot created the root but it's the container DOM node that we've previously passed to the other API. Not the root.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with "passed to"

@@ -160,9 +159,11 @@ setRestoreImplementation(restoreControlledState);
export type DOMContainer =
| (Element & {
_reactRootContainer: ?Root,
_reactIsNewStyleRootDEV: ?boolean,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can read as "new StyleRoot" (and confused me for a moment), I assume you mean "new-style root". maybe reactIsCreateRootDEV?

warningWithoutStack(
!container._reactIsNewStyleRootDEV,
'You are calling ReactDOM.render() on a container that was previously ' +
'managed by ReactDOM.%s(). This is not supported. ' +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created by

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit

@@ -160,9 +159,11 @@ setRestoreImplementation(restoreControlledState);
export type DOMContainer =
| (Element & {
_reactRootContainer: ?Root,
_reactHasBeenPassedToCreateRootDEV: ?boolean,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't need this because _reactRootContainer only exists on legacy roots.

Copy link
Collaborator Author

@gaearon gaearon Jan 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My primary goal was to detect that a container previously passed to createRoot is then passed to unmountComponentAtNode. I don't think I can do this without some kind of a marker on the new container.

I can't check its first child because createRoot doesn't clear children (and so could mount at arbitrary point). Scanning all children seems not great. I don't know of another way to detect that a DOM element is a new-style root.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const reactHasBeenPassedToCreateRoot = domContainer._reactRootContainer === null;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But isn't that used by ReactDOM.render too? Since it's expressed via createRoot internally.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReactDOM.render uses the ReactRoot type, but it doesn't call createRoot directly.

The only place _reactRootContainer is set is in legacyRenderSubtreeIntoContainer. New roots don't need the expando because the root is retained in user space.

root = container._reactRootContainer = legacyCreateRootFromDOMContainer(

Copy link
Collaborator Author

@gaearon gaearon Jan 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're talking about two different cases here.

  1. Detecting DOM container that's previously been used by ReactDOM.render. This is easy. That's what _reactRootContainer gives us.

  2. Detecting DOM container that's previously been used by ReactDOM.createRoot. I don't see a way to do this currently. As you said, "new roots don't need the expando". That's why I couldn't figure out how to detect them, and added a new DEV field.

I need both checks. Not just one way around. In fact (2) is primary motivation for this PR — since Royi was passing createRoot() root to unmountComponentAtNode(). So it's new root that I needed to detect inside an old API.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside unmountComponentAtNode, if _reactRootContainer doesn't exist, that's a mistake. I was figuring you would warn regardless, but I suppose you're trying to distinguish between an empty DOM container that was never rendered by anything, and a DOM container that was rendered by createRoot, so you can provide a more helpful message.

Switching from root.render to ReactDOM.render is more interesting, though, I didn't notice you were warning about that one too. That makes sense and I get why you would need the additional expando for that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you're trying to distinguish between an empty DOM container that was never rendered by anything, and a DOM container that was rendered by createRoot, so you can provide a more helpful message.

Yep 👍

@gaearon gaearon merged commit 17d70df into facebook:master Jan 18, 2019
@gaearon
Copy link
Collaborator Author

gaearon commented Jan 18, 2019

I assume #14615 (comment) makes sense but if it doesn't I can follow up.

@gaearon gaearon deleted the warn-confusing branch January 18, 2019 00:20
jetoneza pushed a commit to jetoneza/react that referenced this pull request Jan 23, 2019
* Warn when mixing createRoot() and old APIs

* Move container checks to entry points

This way further warning check doesn't crash on bad inputs.

* Fix Flow

* Rename flag to be clearer

* managed by -> passed to

* Revert accidental change

* Fix Fire shim to match
n8schloss pushed a commit to n8schloss/react that referenced this pull request Jan 31, 2019
* Warn when mixing createRoot() and old APIs

* Move container checks to entry points

This way further warning check doesn't crash on bad inputs.

* Fix Flow

* Rename flag to be clearer

* managed by -> passed to

* Revert accidental change

* Fix Fire shim to match
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants