From fd06131136955f18534f7c9c87cf95cbbb246b24 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Mon, 9 May 2022 09:02:23 -0700 Subject: [PATCH 1/2] Support Document as a container for hydration and rendering Previously Document was not handled effectively as a container. in particual when hydrating if there was a fallback to client rendering React would attempt to append a new element into the document before clearing out the existing one which errored leaving the application in brokent state. The initial approach I took was to recycle the documentElement and never remove or append it, always just moving it to the right fiber and appending the right children (heady/body) as needed. However in testing a simple approach in modern browsers it seems like treating the documentElement like any other element works fine. This change modifies the clearContainer method to remove the documentElement if the container is a DOCUMENT_NODE. Once the container is cleared React can append a new documentElement via normal means. --- packages/react-dom/src/client/ReactDOMHostConfig.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 4b640055e5b4e..e4973ec667b53 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -672,9 +672,8 @@ export function clearContainer(container: Container): void { if (container.nodeType === ELEMENT_NODE) { ((container: any): Element).textContent = ''; } else if (container.nodeType === DOCUMENT_NODE) { - const body = ((container: any): Document).body; - if (body != null) { - body.textContent = ''; + if (container.documentElement) { + container.removeChild(container.documentElement); } } } From 0215c835128bee2a22c0bf2a51430140e5739e98 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Mon, 9 May 2022 09:03:14 -0700 Subject: [PATCH 2/2] Allow Document as container for createRoot previously rendering into Document was broken and only hydration worked because React did not properly deal with the documentElement and would error in a broken state if used that way. With the previous commit addressing this limitation this change re-adds Document as a valid container for createRoot. It should be noted that if you use document with createRoot it will drop anything a 3rd party scripts adds the page before rendering for the first time. --- packages/react-dom/client.js | 2 +- packages/react-dom/src/client/ReactDOM.js | 2 +- packages/react-dom/src/client/ReactDOMRoot.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-dom/client.js b/packages/react-dom/client.js index 2234cc0a76ebe..0992c68d5b8e5 100644 --- a/packages/react-dom/client.js +++ b/packages/react-dom/client.js @@ -23,7 +23,7 @@ import { } from './'; export function createRoot( - container: Element | DocumentFragment, + container: Element | Document | DocumentFragment, options?: CreateRootOptions, ): RootType { if (__DEV__) { diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 04355605ed57b..1c664588407bc 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -148,7 +148,7 @@ const Internals = { }; function createRoot( - container: Element | DocumentFragment, + container: Element | Document | DocumentFragment, options?: CreateRootOptions, ): RootType { if (__DEV__) { diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 55c340be7dff4..49084d0f9bfaa 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -164,7 +164,7 @@ ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount = funct }; export function createRoot( - container: Element | DocumentFragment, + container: Element | Document | DocumentFragment, options?: CreateRootOptions, ): RootType { if (!isValidContainer(container)) {