From eb1655f4f38f7f2fb30b56bb1555408663ad31e7 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Tue, 4 Aug 2020 10:56:33 +0800 Subject: [PATCH 1/3] fix: render in shadow root --- .../react-dom/src/client/ReactDOMComponent.js | 17 ++++++++++------- .../react-dom/src/client/ReactDOMHostConfig.js | 3 ++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index b82a41eb0faa..21954db36a5d 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -66,6 +66,7 @@ import { DOCUMENT_NODE, ELEMENT_NODE, COMMENT_NODE, + DOCUMENT_FRAGMENT_NODE, } from '../shared/HTMLNodeType'; import isCustomComponent from '../shared/isCustomComponent'; import possibleStandardNames from '../shared/possibleStandardNames'; @@ -256,7 +257,7 @@ if (__DEV__) { } export function ensureListeningTo( - rootContainerInstance: Element | Node, + rootContainerInstance: Element | Node | ShadowRoot, reactPropEvent: string, targetElement: Element | null, ): void { @@ -270,8 +271,10 @@ export function ensureListeningTo( // want to register events to document fragments or documents // with the modern plugin event system. invariant( - rootContainerElement != null && - rootContainerElement.nodeType === ELEMENT_NODE, + (rootContainerElement != null && + rootContainerElement.nodeType === ELEMENT_NODE) || + (rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE && + (rootContainerElement: ShadowRoot).mode), 'ensureListeningTo(): received a container that was not an element node. ' + 'This is likely a bug in React.', ); @@ -308,7 +311,7 @@ export function trapClickOnNonInteractiveElement(node: HTMLElement) { function setInitialDOMProperties( tag: string, domElement: Element, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, nextProps: Object, isCustomComponentTag: boolean, ): void { @@ -510,7 +513,7 @@ export function setInitialProperties( domElement: Element, tag: string, rawProps: Object, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, ): void { const isCustomComponentTag = isCustomComponent(tag, rawProps); if (__DEV__) { @@ -645,7 +648,7 @@ export function diffProperties( tag: string, lastRawProps: Object, nextRawProps: Object, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, ): null | Array { if (__DEV__) { validatePropertiesInDevelopment(tag, nextRawProps); @@ -910,7 +913,7 @@ export function diffHydratedProperties( tag: string, rawProps: Object, parentNamespace: string, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, ): null | Array { let isCustomComponentTag; let extraAttributeNames: Set; diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 22d38dea0439..1ffce1c94d88 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -104,7 +104,8 @@ export type EventTargetChildElement = { }; export type Container = | (Element & {_reactRootContainer?: RootType, ...}) - | (Document & {_reactRootContainer?: RootType, ...}); + | (Document & {_reactRootContainer?: RootType, ...}) + | (ShadowRoot & {_reactRootContainer?: RootType, ...}); export type Instance = Element; export type TextInstance = Text; export type SuspenseInstance = Comment & {_reactRetry?: () => void, ...}; From acf8b96862f031955328148c491dc1ce517cb6ae Mon Sep 17 00:00:00 2001 From: Jack Works Date: Sat, 8 Aug 2020 22:37:05 +0800 Subject: [PATCH 2/3] fix: flow typing --- .../react-dom/src/client/ReactDOMComponent.js | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 21954db36a5d..02499d371240 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -271,10 +271,11 @@ export function ensureListeningTo( // want to register events to document fragments or documents // with the modern plugin event system. invariant( - (rootContainerElement != null && - rootContainerElement.nodeType === ELEMENT_NODE) || - (rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE && - (rootContainerElement: ShadowRoot).mode), + rootContainerElement != null && + (rootContainerElement.nodeType === ELEMENT_NODE || + (rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE && + // $FlowFixMe GH issue #8457 + ((rootContainerElement: any): ShadowRoot).mode)), 'ensureListeningTo(): received a container that was not an element node. ' + 'This is likely a bug in React.', ); @@ -286,7 +287,7 @@ export function ensureListeningTo( } function getOwnerDocumentFromRootContainer( - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, ): Document { return rootContainerElement.nodeType === DOCUMENT_NODE ? (rootContainerElement: any) @@ -396,7 +397,7 @@ function updateDOMProperties( export function createElement( type: string, props: Object, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, parentNamespace: string, ): Element { let isCustomComponentTag; @@ -502,7 +503,7 @@ export function createElement( export function createTextNode( text: string, - rootContainerElement: Element | Document, + rootContainerElement: Element | Document | ShadowRoot, ): Text { return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode( text, @@ -1212,7 +1213,7 @@ export function warnForUnmatchedText(textNode: Text, text: string) { } export function warnForDeletedHydratableElement( - parentNode: Element | Document, + parentNode: Element | Document | ShadowRoot, child: Element, ) { if (__DEV__) { @@ -1229,7 +1230,7 @@ export function warnForDeletedHydratableElement( } export function warnForDeletedHydratableText( - parentNode: Element | Document, + parentNode: Element | Document | ShadowRoot, child: Text, ) { if (__DEV__) { @@ -1246,7 +1247,7 @@ export function warnForDeletedHydratableText( } export function warnForInsertedHydratedElement( - parentNode: Element | Document, + parentNode: Element | Document | ShadowRoot, tag: string, props: Object, ) { @@ -1264,7 +1265,7 @@ export function warnForInsertedHydratedElement( } export function warnForInsertedHydratedText( - parentNode: Element | Document, + parentNode: Element | Document | ShadowRoot, text: string, ) { if (__DEV__) { From 822541cecb027c6623c33d242eda14221c30e86a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 17 Aug 2020 15:34:08 +0100 Subject: [PATCH 3/3] Remove types and turn invariant into warning --- .../react-dom/src/client/ReactDOMComponent.js | 50 +++++++++---------- .../src/client/ReactDOMHostConfig.js | 3 +- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 02499d371240..d3e72d2a2d21 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -13,7 +13,6 @@ import { } from '../events/EventRegistry'; import {canUseDOM} from 'shared/ExecutionEnvironment'; -import invariant from 'shared/invariant'; import { getValueForAttribute, @@ -257,7 +256,7 @@ if (__DEV__) { } export function ensureListeningTo( - rootContainerInstance: Element | Node | ShadowRoot, + rootContainerInstance: Element | Node, reactPropEvent: string, targetElement: Element | null, ): void { @@ -267,18 +266,19 @@ export function ensureListeningTo( rootContainerInstance.nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance; - // Containers should only ever be element nodes. We do not - // want to register events to document fragments or documents - // with the modern plugin event system. - invariant( - rootContainerElement != null && - (rootContainerElement.nodeType === ELEMENT_NODE || - (rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE && - // $FlowFixMe GH issue #8457 - ((rootContainerElement: any): ShadowRoot).mode)), - 'ensureListeningTo(): received a container that was not an element node. ' + - 'This is likely a bug in React.', - ); + if (__DEV__) { + if ( + rootContainerElement == null || + (rootContainerElement.nodeType !== ELEMENT_NODE && + // This is to support rendering into a ShadowRoot: + rootContainerElement.nodeType !== DOCUMENT_FRAGMENT_NODE) + ) { + console.error( + 'ensureListeningTo(): received a container that was not an element node. ' + + 'This is likely a bug in React. Please file an issue.', + ); + } + } listenToReactEvent( reactPropEvent, ((rootContainerElement: any): Element), @@ -287,7 +287,7 @@ export function ensureListeningTo( } function getOwnerDocumentFromRootContainer( - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, ): Document { return rootContainerElement.nodeType === DOCUMENT_NODE ? (rootContainerElement: any) @@ -312,7 +312,7 @@ export function trapClickOnNonInteractiveElement(node: HTMLElement) { function setInitialDOMProperties( tag: string, domElement: Element, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, nextProps: Object, isCustomComponentTag: boolean, ): void { @@ -397,7 +397,7 @@ function updateDOMProperties( export function createElement( type: string, props: Object, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, parentNamespace: string, ): Element { let isCustomComponentTag; @@ -503,7 +503,7 @@ export function createElement( export function createTextNode( text: string, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, ): Text { return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode( text, @@ -514,7 +514,7 @@ export function setInitialProperties( domElement: Element, tag: string, rawProps: Object, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, ): void { const isCustomComponentTag = isCustomComponent(tag, rawProps); if (__DEV__) { @@ -649,7 +649,7 @@ export function diffProperties( tag: string, lastRawProps: Object, nextRawProps: Object, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, ): null | Array { if (__DEV__) { validatePropertiesInDevelopment(tag, nextRawProps); @@ -914,7 +914,7 @@ export function diffHydratedProperties( tag: string, rawProps: Object, parentNamespace: string, - rootContainerElement: Element | Document | ShadowRoot, + rootContainerElement: Element | Document, ): null | Array { let isCustomComponentTag; let extraAttributeNames: Set; @@ -1213,7 +1213,7 @@ export function warnForUnmatchedText(textNode: Text, text: string) { } export function warnForDeletedHydratableElement( - parentNode: Element | Document | ShadowRoot, + parentNode: Element | Document, child: Element, ) { if (__DEV__) { @@ -1230,7 +1230,7 @@ export function warnForDeletedHydratableElement( } export function warnForDeletedHydratableText( - parentNode: Element | Document | ShadowRoot, + parentNode: Element | Document, child: Text, ) { if (__DEV__) { @@ -1247,7 +1247,7 @@ export function warnForDeletedHydratableText( } export function warnForInsertedHydratedElement( - parentNode: Element | Document | ShadowRoot, + parentNode: Element | Document, tag: string, props: Object, ) { @@ -1265,7 +1265,7 @@ export function warnForInsertedHydratedElement( } export function warnForInsertedHydratedText( - parentNode: Element | Document | ShadowRoot, + parentNode: Element | Document, text: string, ) { if (__DEV__) { diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 1ffce1c94d88..22d38dea0439 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -104,8 +104,7 @@ export type EventTargetChildElement = { }; export type Container = | (Element & {_reactRootContainer?: RootType, ...}) - | (Document & {_reactRootContainer?: RootType, ...}) - | (ShadowRoot & {_reactRootContainer?: RootType, ...}); + | (Document & {_reactRootContainer?: RootType, ...}); export type Instance = Element; export type TextInstance = Text; export type SuspenseInstance = Comment & {_reactRetry?: () => void, ...};