diff --git a/packages/react-dom-bindings/src/client/ReactDOMFloatClient.js b/packages/react-dom-bindings/src/client/ReactDOMFloatClient.js index c9e8607eaaf7d..55a37d240351b 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMFloatClient.js +++ b/packages/react-dom-bindings/src/client/ReactDOMFloatClient.js @@ -34,6 +34,8 @@ import { getHostContext, } from 'react-reconciler/src/ReactFiberHostContext'; import {getResourceFormOnly} from './validateDOMNesting'; +import {getNamespace} from './ReactDOMHostConfig'; +import {SVG_NAMESPACE} from '../shared/DOMNamespaces'; // The resource types we support. currently they match the form for the as argument. // In the future this may need to change, especially when modules / scripts are supported @@ -201,6 +203,28 @@ function getCurrentResourceRoot(): null | FloatRoot { return currentContainer ? currentContainer.getRootNode() : null; } +// This resource type constraint can be loosened. It really is everything except PreloadResource +// because that is the only one that does not have an optional instance type. Expand as needed. +function resetInstance(resource: ScriptResource | HeadResource) { + resource.instance = undefined; +} + +export function clearRootResources(rootContainer: Container): void { + const rootNode: FloatRoot = (rootContainer.getRootNode(): any); + const resources = getResourcesFromRoot(rootNode); + + // We can't actually delete the resource cache because this function is called + // during commit after we have rendered. Instead we detatch any instances from + // the Resource object if they are going to be cleared + + // Styles stay put + // Scripts get reset + resources.scripts.forEach(resetInstance); + // Head Resources get reset + resources.head.forEach(resetInstance); + // lastStructuredMeta stays put +} + // Preloads are somewhat special. Even if we don't have the Document // used by the root that is rendering a component trying to insert a preload // we can still seed the file cache by doing the preload on any document we have @@ -1077,7 +1101,14 @@ function acquireHeadResource(resource: HeadResource): Instance { props, root, ); - insertResourceInstanceBefore(root, instance, titles.item(0)); + const firstTitle = titles[0]; + insertResourceInstanceBefore( + root, + instance, + firstTitle && firstTitle.namespaceURI !== SVG_NAMESPACE + ? firstTitle + : null, + ); break; } case 'meta': { @@ -1397,16 +1428,21 @@ function insertResourceInstanceBefore( export function isHostResourceType(type: string, props: Props): boolean { let resourceFormOnly: boolean; + let namespace: string; if (__DEV__) { const hostContext = getHostContext(); resourceFormOnly = getResourceFormOnly(hostContext); + namespace = getNamespace(hostContext); } switch (type) { case 'base': - case 'meta': - case 'title': { + case 'meta': { return true; } + case 'title': { + const hostContext = getHostContext(); + return getNamespace(hostContext) !== SVG_NAMESPACE; + } case 'link': { const {onLoad, onError} = props; if (onLoad || onError) { @@ -1417,6 +1453,11 @@ export function isHostResourceType(type: string, props: Props): boolean { ' Try removing onLoad={...} and onError={...} or moving it into the root
tag or' + ' somewhere in the .', ); + } else if (namespace === SVG_NAMESPACE) { + console.error( + 'Cannot render a with onLoad or onError listeners as a descendent of