mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
support ReactDOM.render(..., document) without crashing (#26129)
as reported in #26128 `ReactDOM.render(..., document)` crashed when `enableHostSingletons` was on. This is because it had a different way of clearing the container than `createRoot(document)`. I updated the legacy implementation to share the clearing behavior of `creatRoot` which will preserve the singleton instances. I also removed the warning saying not to use `document.body` as a container
This commit is contained in:
@@ -13,6 +13,7 @@ let JSDOM;
|
||||
let Stream;
|
||||
let Scheduler;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMClient;
|
||||
let ReactDOMFizzServer;
|
||||
let document;
|
||||
@@ -28,6 +29,7 @@ describe('ReactDOM HostSingleton', () => {
|
||||
JSDOM = require('jsdom').JSDOM;
|
||||
Scheduler = require('scheduler');
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMClient = require('react-dom/client');
|
||||
ReactDOMFizzServer = require('react-dom/server');
|
||||
Stream = require('stream');
|
||||
@@ -1007,4 +1009,19 @@ describe('ReactDOM HostSingleton', () => {
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/facebook/react/issues/26128
|
||||
it('(#26128) does not throw when rendering at body', async () => {
|
||||
ReactDOM.render(<div />, document.body);
|
||||
});
|
||||
|
||||
// https://github.com/facebook/react/issues/26128
|
||||
it('(#26128) does not throw when rendering at <html>', async () => {
|
||||
ReactDOM.render(<body />, document.documentElement);
|
||||
});
|
||||
|
||||
// https://github.com/facebook/react/issues/26128
|
||||
it('(#26128) does not throw when rendering at document', async () => {
|
||||
ReactDOM.render(<html />, document);
|
||||
});
|
||||
});
|
||||
|
||||
+11
-6
@@ -149,12 +149,17 @@ describe('ReactMount', () => {
|
||||
const iFrame = document.createElement('iframe');
|
||||
document.body.appendChild(iFrame);
|
||||
|
||||
expect(() =>
|
||||
ReactDOM.render(<div />, iFrame.contentDocument.body),
|
||||
).toErrorDev(
|
||||
'Rendering components directly into document.body is discouraged',
|
||||
{withoutStack: true},
|
||||
);
|
||||
if (gate(flags => flags.enableHostSingletons)) {
|
||||
// HostSingletons make the warning for document.body unecessary
|
||||
ReactDOM.render(<div />, iFrame.contentDocument.body);
|
||||
} else {
|
||||
expect(() =>
|
||||
ReactDOM.render(<div />, iFrame.contentDocument.body),
|
||||
).toErrorDev(
|
||||
'Rendering components directly into document.body is discouraged',
|
||||
{withoutStack: true},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should account for escaping on a checksum mismatch', () => {
|
||||
|
||||
@@ -28,9 +28,11 @@ function expectWarnings(tags, warnings = [], withoutStack = 0) {
|
||||
element = <Tag>{element}</Tag>;
|
||||
}
|
||||
|
||||
expect(() => ReactDOM.render(element, container)).toErrorDev(warnings, {
|
||||
withoutStack,
|
||||
});
|
||||
if (warnings.length) {
|
||||
expect(() => ReactDOM.render(element, container)).toErrorDev(warnings, {
|
||||
withoutStack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('validateDOMNesting', () => {
|
||||
@@ -39,8 +41,10 @@ describe('validateDOMNesting', () => {
|
||||
expectWarnings(
|
||||
['body', 'datalist', 'option'],
|
||||
[
|
||||
'render(): Rendering components directly into document.body is discouraged',
|
||||
],
|
||||
gate(flags => !flags.enableHostSingletons)
|
||||
? 'render(): Rendering components directly into document.body is discouraged'
|
||||
: null,
|
||||
].filter(Boolean),
|
||||
1,
|
||||
);
|
||||
expectWarnings(['div', 'a', 'object', 'a']);
|
||||
@@ -106,13 +110,9 @@ describe('validateDOMNesting', () => {
|
||||
expectWarnings(
|
||||
['body', 'body'],
|
||||
[
|
||||
'render(): Rendering components directly into document.body is discouraged',
|
||||
'validateDOMNesting(...): <body> cannot appear as a child of <body>.\n' +
|
||||
' in body (at **)',
|
||||
'Warning: You are mounting a new body component when a previous one has not first unmounted. It is an error to render more than one body component at a time and attributes and children of these components will likely fail in unpredictable ways. Please only render a single instance of <body> and if you need to mount a new one, ensure any previous ones have unmounted first.\n' +
|
||||
' in body (at **)',
|
||||
],
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
expectWarnings(
|
||||
|
||||
+4
-4
@@ -14,6 +14,7 @@ import type {
|
||||
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
|
||||
import {clearContainer} from 'react-dom-bindings/src/client/ReactDOMHostConfig';
|
||||
import {
|
||||
getInstanceFromNode,
|
||||
isContainerMarkedAsRoot,
|
||||
@@ -42,6 +43,7 @@ import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
|
||||
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {has as hasInstance} from 'shared/ReactInstanceMap';
|
||||
import {enableHostSingletons} from '../../../shared/ReactFeatureFlags';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
|
||||
@@ -79,6 +81,7 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
if (
|
||||
!enableHostSingletons &&
|
||||
container.nodeType === ELEMENT_NODE &&
|
||||
((container: any): Element).tagName &&
|
||||
((container: any): Element).tagName.toUpperCase() === 'BODY'
|
||||
@@ -152,10 +155,7 @@ function legacyCreateRootFromDOMContainer(
|
||||
return root;
|
||||
} else {
|
||||
// First clear any existing content.
|
||||
let rootSibling;
|
||||
while ((rootSibling = container.lastChild)) {
|
||||
container.removeChild(rootSibling);
|
||||
}
|
||||
clearContainer(container);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
const originalCallback = callback;
|
||||
|
||||
Reference in New Issue
Block a user