diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index ebfe403f2315d..b3293ea83bd8a 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -7,7 +7,6 @@ * @flow */ -import type {HostDispatcher} from 'react-dom/src/shared/ReactDOMTypes'; import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities'; import type {DOMEventName} from '../events/DOMEventNames'; import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; @@ -107,6 +106,10 @@ import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem'; import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation'; import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes'; +import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; +const ReactDOMCurrentDispatcher = + ReactDOMSharedInternals.ReactDOMCurrentDispatcher; + export type Type = string; export type Props = { autoFocus?: boolean, @@ -2108,10 +2111,8 @@ function getDocumentFromRoot(root: HoistableRoot): Document { return root.ownerDocument || root; } -// We want this to be the default dispatcher on ReactDOMSharedInternals but we don't want to mutate -// internals in Module scope. Instead we export it and Internals will import it. There is already a cycle -// from Internals -> ReactDOM -> HostConfig -> Internals so this doesn't introduce a new one. -export const ReactDOMClientDispatcher: HostDispatcher = { +const previousDispatcher = ReactDOMCurrentDispatcher.current; +ReactDOMCurrentDispatcher.current = { prefetchDNS, preconnect, preload, @@ -2127,8 +2128,9 @@ export const ReactDOMClientDispatcher: HostDispatcher = { // and so we have to fall back to something universal. Currently we just refer to the global document. // This is notable because nowhere else in ReactDOM do we actually reference the global document or window // because we may be rendering inside an iframe. -function getDocumentForImperativeFloatMethods(): Document { - return document; +const globalDocument = typeof document === 'undefined' ? null : document; +function getGlobalDocument(): ?Document { + return globalDocument; } function preconnectAs( @@ -2136,8 +2138,8 @@ function preconnectAs( href: string, crossOrigin: ?CrossOriginEnum, ) { - const ownerDocument = getDocumentForImperativeFloatMethods(); - if (typeof href === 'string' && href) { + const ownerDocument = getGlobalDocument(); + if (ownerDocument && typeof href === 'string' && href) { const limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes(href); let key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`; @@ -2162,6 +2164,7 @@ function prefetchDNS(href: string) { if (!enableFloat) { return; } + previousDispatcher.prefetchDNS(href); preconnectAs('dns-prefetch', href, null); } @@ -2169,6 +2172,7 @@ function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) { if (!enableFloat) { return; } + previousDispatcher.preconnect(href, crossOrigin); preconnectAs('preconnect', href, crossOrigin); } @@ -2176,8 +2180,9 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) { if (!enableFloat) { return; } - const ownerDocument = getDocumentForImperativeFloatMethods(); - if (href && as && ownerDocument) { + previousDispatcher.preload(href, as, options); + const ownerDocument = getGlobalDocument(); + if (ownerDocument && href && as) { let preloadSelector = `link[rel="preload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes( as, )}"]`; @@ -2256,8 +2261,9 @@ function preloadModule(href: string, options?: ?PreloadModuleImplOptions) { if (!enableFloat) { return; } - const ownerDocument = getDocumentForImperativeFloatMethods(); - if (href) { + previousDispatcher.preloadModule(href, options); + const ownerDocument = getGlobalDocument(); + if (ownerDocument && href) { const as = options && typeof options.as === 'string' ? options.as : 'script'; const preloadSelector = `link[rel="modulepreload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes( @@ -2319,9 +2325,10 @@ function preinitStyle( if (!enableFloat) { return; } - const ownerDocument = getDocumentForImperativeFloatMethods(); + previousDispatcher.preinitStyle(href, precedence, options); - if (href) { + const ownerDocument = getGlobalDocument(); + if (ownerDocument && href) { const styles = getResourcesFromRoot(ownerDocument).hoistableStyles; const key = getStyleKey(href); @@ -2395,9 +2402,10 @@ function preinitScript(src: string, options?: ?PreinitScriptOptions) { if (!enableFloat) { return; } - const ownerDocument = getDocumentForImperativeFloatMethods(); + previousDispatcher.preinitScript(src, options); - if (src) { + const ownerDocument = getGlobalDocument(); + if (ownerDocument && src) { const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts; const key = getScriptKey(src); @@ -2453,9 +2461,10 @@ function preinitModuleScript( if (!enableFloat) { return; } - const ownerDocument = getDocumentForImperativeFloatMethods(); + previousDispatcher.preinitModuleScript(src, options); - if (src) { + const ownerDocument = getGlobalDocument(); + if (ownerDocument && src) { const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts; const key = getScriptKey(src); diff --git a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js index 36913899bb030..5cd5dbfb6dbbd 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js +++ b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js @@ -8,7 +8,6 @@ */ import type { - HostDispatcher, CrossOriginEnum, PreloadImplOptions, PreloadModuleImplOptions, @@ -25,7 +24,12 @@ import { resolveRequest, } from 'react-server/src/ReactFlightServer'; -export const ReactDOMFlightServerDispatcher: HostDispatcher = { +import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; +const ReactDOMCurrentDispatcher = + ReactDOMSharedInternals.ReactDOMCurrentDispatcher; + +const previousDispatcher = ReactDOMCurrentDispatcher.current; +ReactDOMCurrentDispatcher.current = { prefetchDNS, preconnect, preload, @@ -48,6 +52,8 @@ function prefetchDNS(href: string) { } hints.add(key); emitHint(request, 'D', href); + } else { + previousDispatcher.prefetchDNS(href); } } } @@ -71,6 +77,8 @@ function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) { } else { emitHint(request, 'C', href); } + } else { + previousDispatcher.preconnect(href, crossOrigin); } } } @@ -104,6 +112,8 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) { } else { emitHint(request, 'L', [href, as]); } + } else { + previousDispatcher.preload(href, as, options); } } } @@ -128,6 +138,8 @@ function preloadModule(href: string, options?: ?PreloadModuleImplOptions) { } else { return emitHint(request, 'm', href); } + } else { + previousDispatcher.preloadModule(href, options); } } } @@ -162,18 +174,20 @@ function preinitStyle( } else { return emitHint(request, 'S', href); } + } else { + previousDispatcher.preinitStyle(href, precedence, options); } } } } -function preinitScript(href: string, options?: ?PreinitScriptOptions) { +function preinitScript(src: string, options?: ?PreinitScriptOptions) { if (enableFloat) { - if (typeof href === 'string') { + if (typeof src === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); - const key = 'X|' + href; + const key = 'X|' + src; if (hints.has(key)) { // duplicate hint return; @@ -182,25 +196,27 @@ function preinitScript(href: string, options?: ?PreinitScriptOptions) { const trimmed = trimOptions(options); if (trimmed) { - return emitHint(request, 'X', [href, trimmed]); + return emitHint(request, 'X', [src, trimmed]); } else { - return emitHint(request, 'X', href); + return emitHint(request, 'X', src); } + } else { + previousDispatcher.preinitScript(src, options); } } } } function preinitModuleScript( - href: string, + src: string, options?: ?PreinitModuleScriptOptions, ) { if (enableFloat) { - if (typeof href === 'string') { + if (typeof src === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); - const key = 'M|' + href; + const key = 'M|' + src; if (hints.has(key)) { // duplicate hint return; @@ -209,10 +225,12 @@ function preinitModuleScript( const trimmed = trimOptions(options); if (trimmed) { - return emitHint(request, 'M', [href, trimmed]); + return emitHint(request, 'M', [src, trimmed]); } else { - return emitHint(request, 'M', href); + return emitHint(request, 'M', src); } + } else { + previousDispatcher.preinitModuleScript(src, options); } } } diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 0410f9f042211..5902ece4d718e 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -88,22 +88,20 @@ import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMRes import {NotPending} from '../shared/ReactDOMFormActions'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; -const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; +const ReactDOMCurrentDispatcher = + ReactDOMSharedInternals.ReactDOMCurrentDispatcher; -const ReactDOMServerDispatcher = { +const previousDispatcher = ReactDOMCurrentDispatcher.current; +ReactDOMCurrentDispatcher.current = { prefetchDNS, preconnect, preload, preloadModule, - preinitStyle, preinitScript, + preinitStyle, preinitModuleScript, }; -export function prepareHostDispatcher() { - ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher; -} - // We make every property of the descriptor optional because it is not a contract that // the headers provided by onHeaders has any particular header types. export type HeadersDescriptor = { @@ -5342,6 +5340,7 @@ function prefetchDNS(href: string) { // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.prefetchDNS(href); return; } const resumableState = getResumableState(request); @@ -5397,6 +5396,7 @@ function preconnect(href: string, crossOrigin: ?CrossOriginEnum) { // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preconnect(href, crossOrigin); return; } const resumableState = getResumableState(request); @@ -5460,6 +5460,7 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) { // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preload(href, as, options); return; } const resumableState = getResumableState(request); @@ -5663,6 +5664,7 @@ function preloadModule( // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preloadModule(href, options); return; } const resumableState = getResumableState(request); @@ -5739,6 +5741,7 @@ function preinitStyle( // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preinitStyle(href, precedence, options); return; } const resumableState = getResumableState(request); @@ -5826,6 +5829,7 @@ function preinitScript(src: string, options?: ?PreinitScriptOptions): void { // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preinitScript(src, options); return; } const resumableState = getResumableState(request); @@ -5891,6 +5895,7 @@ function preinitModuleScript( // the resources for this call in either case we opt to do nothing. We can consider making this a warning // but there may be times where calling a function outside of render is intentional (i.e. to warm up data // fetching) and we don't want to warn in those cases. + previousDispatcher.preinitModuleScript(src, options); return; } const resumableState = getResumableState(request); diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js index a8f38e283cefc..251738aa2f0a5 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js @@ -163,7 +163,6 @@ export { writeHoistables, writePostamble, hoistHoistables, - prepareHostDispatcher, resetResumableState, completeResumableState, emitEarlyPreloads, diff --git a/packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js index 336b1d3ef412b..32905aa45b1fb 100644 --- a/packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js @@ -16,14 +16,9 @@ import type { PreinitModuleScriptOptions, } from 'react-dom/src/shared/ReactDOMTypes'; -import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; -const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; - -import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher'; - -export function prepareHostDispatcher(): void { - ReactDOMCurrentDispatcher.current = ReactDOMFlightServerDispatcher; -} +// This module registers the host dispatcher so it needs to be imported +// but it does not have any exports +import './ReactDOMFlightServerHostDispatcher'; // Used to distinguish these contexts from ones used in other renderers. // E.g. this can be used to distinguish legacy renderers from this modern one. diff --git a/packages/react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js b/packages/react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js index 18ef25b06842e..61356ce060a53 100644 --- a/packages/react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js +++ b/packages/react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js @@ -13,7 +13,8 @@ import type {HintCode, HintModel} from '../server/ReactFlightServerConfigDOM'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; -const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; +const ReactDOMCurrentDispatcher = + ReactDOMSharedInternals.ReactDOMCurrentDispatcher; import {getCrossOriginString} from './crossOriginStrings'; @@ -22,87 +23,85 @@ export function dispatchHint( model: HintModel, ): void { const dispatcher = ReactDOMCurrentDispatcher.current; - if (dispatcher) { - switch (code) { - case 'D': { - const refined = refineModel(code, model); + switch (code) { + case 'D': { + const refined = refineModel(code, model); + const href = refined; + dispatcher.prefetchDNS(href); + return; + } + case 'C': { + const refined = refineModel(code, model); + if (typeof refined === 'string') { const href = refined; - dispatcher.prefetchDNS(href); - return; + dispatcher.preconnect(href); + } else { + const href = refined[0]; + const crossOrigin = refined[1]; + dispatcher.preconnect(href, crossOrigin); } - case 'C': { - const refined = refineModel(code, model); - if (typeof refined === 'string') { - const href = refined; - dispatcher.preconnect(href); - } else { - const href = refined[0]; - const crossOrigin = refined[1]; - dispatcher.preconnect(href, crossOrigin); - } - return; + return; + } + case 'L': { + const refined = refineModel(code, model); + const href = refined[0]; + const as = refined[1]; + if (refined.length === 3) { + const options = refined[2]; + dispatcher.preload(href, as, options); + } else { + dispatcher.preload(href, as); } - case 'L': { - const refined = refineModel(code, model); + return; + } + case 'm': { + const refined = refineModel(code, model); + if (typeof refined === 'string') { + const href = refined; + dispatcher.preloadModule(href); + } else { const href = refined[0]; - const as = refined[1]; - if (refined.length === 3) { - const options = refined[2]; - dispatcher.preload(href, as, options); - } else { - dispatcher.preload(href, as); - } - return; + const options = refined[1]; + dispatcher.preloadModule(href, options); } - case 'm': { - const refined = refineModel(code, model); - if (typeof refined === 'string') { - const href = refined; - dispatcher.preloadModule(href); - } else { - const href = refined[0]; - const options = refined[1]; - dispatcher.preloadModule(href, options); - } - return; - } - case 'S': { - const refined = refineModel(code, model); - if (typeof refined === 'string') { - const href = refined; - dispatcher.preinitStyle(href); - } else { - const href = refined[0]; - const precedence = refined[1] === 0 ? undefined : refined[1]; - const options = refined.length === 3 ? refined[2] : undefined; - dispatcher.preinitStyle(href, precedence, options); - } - return; + return; + } + case 'S': { + const refined = refineModel(code, model); + if (typeof refined === 'string') { + const href = refined; + dispatcher.preinitStyle(href); + } else { + const href = refined[0]; + const precedence = refined[1] === 0 ? undefined : refined[1]; + const options = refined.length === 3 ? refined[2] : undefined; + dispatcher.preinitStyle(href, precedence, options); } - case 'X': { - const refined = refineModel(code, model); - if (typeof refined === 'string') { - const href = refined; - dispatcher.preinitScript(href); - } else { - const href = refined[0]; - const options = refined[1]; - dispatcher.preinitScript(href, options); - } - return; + return; + } + case 'X': { + const refined = refineModel(code, model); + if (typeof refined === 'string') { + const href = refined; + dispatcher.preinitScript(href); + } else { + const href = refined[0]; + const options = refined[1]; + dispatcher.preinitScript(href, options); } - case 'M': { - const refined = refineModel(code, model); - if (typeof refined === 'string') { - const href = refined; - dispatcher.preinitModuleScript(href); - } else { - const href = refined[0]; - const options = refined[1]; - dispatcher.preinitModuleScript(href, options); - } - return; + return; + } + case 'M': { + const refined = refineModel(code, model); + if (typeof refined === 'string') { + const href = refined; + dispatcher.preinitModuleScript(href); + } else { + const href = refined[0]; + const options = refined[1]; + dispatcher.preinitModuleScript(href, options); } + return; } } } diff --git a/packages/react-dom/src/ReactDOMSharedInternals.js b/packages/react-dom/src/ReactDOMSharedInternals.js index b082b4a19cc27..9f721b718b819 100644 --- a/packages/react-dom/src/ReactDOMSharedInternals.js +++ b/packages/react-dom/src/ReactDOMSharedInternals.js @@ -12,16 +12,28 @@ import type {HostDispatcher} from './shared/ReactDOMTypes'; type InternalsType = { usingClientEntryPoint: boolean, Events: [any, any, any, any, any, any], - Dispatcher: { - current: null | HostDispatcher, + ReactDOMCurrentDispatcher: { + current: HostDispatcher, }, }; +function noop() {} + +const DefaultDispatcher: HostDispatcher = { + prefetchDNS: noop, + preconnect: noop, + preload: noop, + preloadModule: noop, + preinitScript: noop, + preinitStyle: noop, + preinitModuleScript: noop, +}; + const Internals: InternalsType = ({ usingClientEntryPoint: false, Events: null, - Dispatcher: { - current: null, + ReactDOMCurrentDispatcher: { + current: DefaultDispatcher, }, }: any); diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index bbbde79e05fad..563fd6be1fc0c 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -13,24 +13,15 @@ import type { TransitionTracingCallbacks, } from 'react-reconciler/src/ReactInternalTypes'; -import {ReactDOMClientDispatcher} from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; import {queueExplicitHydrationTarget} from 'react-dom-bindings/src/events/ReactDOMEventReplaying'; import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; import { - enableFloat, allowConcurrentByDefault, disableCommentsAsDOMContainers, enableAsyncActions, enableFormActions, } from 'shared/ReactFeatureFlags'; -import ReactDOMSharedInternals from '../ReactDOMSharedInternals'; -const {Dispatcher} = ReactDOMSharedInternals; -if (enableFloat && typeof document !== 'undefined') { - // Set the default dispatcher to the client dispatcher - Dispatcher.current = ReactDOMClientDispatcher; -} - export type RootType = { render(children: ReactNodeList): void, unmount(): void, @@ -228,7 +219,6 @@ export function createRoot( transitionCallbacks, ); markContainerAsRoot(root.current, container); - Dispatcher.current = ReactDOMClientDispatcher; const rootContainerElement: Document | Element | DocumentFragment = container.nodeType === COMMENT_NODE @@ -322,7 +312,6 @@ export function hydrateRoot( formState, ); markContainerAsRoot(root.current, container); - Dispatcher.current = ReactDOMClientDispatcher; // This can't be a comment node since hydration doesn't work on comment nodes anyway. listenToAllSupportedEvents(container); diff --git a/packages/react-dom/src/shared/ReactDOMFloat.js b/packages/react-dom/src/shared/ReactDOMFloat.js index 74701e761179d..2effe174416be 100644 --- a/packages/react-dom/src/shared/ReactDOMFloat.js +++ b/packages/react-dom/src/shared/ReactDOMFloat.js @@ -15,7 +15,8 @@ import type { } from './ReactDOMTypes'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; -const Dispatcher = ReactDOMSharedInternals.Dispatcher; +const ReactDOMCurrentDispatcher = + ReactDOMSharedInternals.ReactDOMCurrentDispatcher; import { getCrossOriginString, @@ -47,9 +48,8 @@ export function prefetchDNS(href: string) { } } } - const dispatcher = Dispatcher.current; - if (dispatcher && typeof href === 'string') { - dispatcher.prefetchDNS(href); + if (typeof href === 'string') { + ReactDOMCurrentDispatcher.current.prefetchDNS(href); } // We don't error because preconnect needs to be resilient to being called in a variety of scopes // and the runtime may not be capable of responding. The function is optimistic and not critical @@ -75,12 +75,11 @@ export function preconnect(href: string, options?: ?PreconnectOptions) { ); } } - const dispatcher = Dispatcher.current; - if (dispatcher && typeof href === 'string') { + if (typeof href === 'string') { const crossOrigin = options ? getCrossOriginString(options.crossOrigin) : null; - dispatcher.preconnect(href, crossOrigin); + ReactDOMCurrentDispatcher.current.preconnect(href, crossOrigin); } // We don't error because preconnect needs to be resilient to being called in a variety of scopes // and the runtime may not be capable of responding. The function is optimistic and not critical @@ -111,9 +110,7 @@ export function preload(href: string, options: PreloadOptions) { ); } } - const dispatcher = Dispatcher.current; if ( - dispatcher && typeof href === 'string' && // We check existence because we cannot enforce this function is actually called with the stated type typeof options === 'object' && @@ -122,7 +119,7 @@ export function preload(href: string, options: PreloadOptions) { ) { const as = options.as; const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin); - dispatcher.preload(href, as, { + ReactDOMCurrentDispatcher.current.preload(href, as, { crossOrigin, integrity: typeof options.integrity === 'string' ? options.integrity : undefined, @@ -173,14 +170,13 @@ export function preloadModule(href: string, options?: ?PreloadModuleOptions) { ); } } - const dispatcher = Dispatcher.current; - if (dispatcher && typeof href === 'string') { + if (typeof href === 'string') { if (options) { const crossOrigin = getCrossOriginStringAs( options.as, options.crossOrigin, ); - dispatcher.preloadModule(href, { + ReactDOMCurrentDispatcher.current.preloadModule(href, { as: typeof options.as === 'string' && options.as !== 'script' ? options.as @@ -190,7 +186,7 @@ export function preloadModule(href: string, options?: ?PreloadModuleOptions) { typeof options.integrity === 'string' ? options.integrity : undefined, }); } else { - dispatcher.preloadModule(href); + ReactDOMCurrentDispatcher.current.preloadModule(href); } } // We don't error because preload needs to be resilient to being called in a variety of scopes @@ -217,13 +213,7 @@ export function preinit(href: string, options: PreinitOptions) { ); } } - const dispatcher = Dispatcher.current; - if ( - dispatcher && - typeof href === 'string' && - options && - typeof options.as === 'string' - ) { + if (typeof href === 'string' && options && typeof options.as === 'string') { const as = options.as; const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin); const integrity = @@ -233,7 +223,7 @@ export function preinit(href: string, options: PreinitOptions) { ? options.fetchPriority : undefined; if (as === 'style') { - dispatcher.preinitStyle( + ReactDOMCurrentDispatcher.current.preinitStyle( href, typeof options.precedence === 'string' ? options.precedence : undefined, { @@ -243,7 +233,7 @@ export function preinit(href: string, options: PreinitOptions) { }, ); } else if (as === 'script') { - dispatcher.preinitScript(href, { + ReactDOMCurrentDispatcher.current.preinitScript(href, { crossOrigin, integrity, fetchPriority, @@ -301,15 +291,14 @@ export function preinitModule(href: string, options?: ?PreinitModuleOptions) { } } } - const dispatcher = Dispatcher.current; - if (dispatcher && typeof href === 'string') { + if (typeof href === 'string') { if (typeof options === 'object' && options !== null) { if (options.as == null || options.as === 'script') { const crossOrigin = getCrossOriginStringAs( options.as, options.crossOrigin, ); - dispatcher.preinitModuleScript(href, { + ReactDOMCurrentDispatcher.current.preinitModuleScript(href, { crossOrigin, integrity: typeof options.integrity === 'string' @@ -319,7 +308,7 @@ export function preinitModule(href: string, options?: ?PreinitModuleOptions) { }); } } else if (options == null) { - dispatcher.preinitModuleScript(href); + ReactDOMCurrentDispatcher.current.preinitModuleScript(href); } } // We don't error because preinit needs to be resilient to being called in a variety of scopes diff --git a/packages/react-dom/src/shared/ReactDOMTypes.js b/packages/react-dom/src/shared/ReactDOMTypes.js index 942515be5c81a..7578e182ae7c8 100644 --- a/packages/react-dom/src/shared/ReactDOMTypes.js +++ b/packages/react-dom/src/shared/ReactDOMTypes.js @@ -90,7 +90,7 @@ export type HostDispatcher = { precedence: ?string, options?: ?PreinitStyleOptions, ) => void, - preinitScript: (src: string, options?: PreinitScriptOptions) => void, + preinitScript: (src: string, options?: ?PreinitScriptOptions) => void, preinitModuleScript: ( src: string, options?: ?PreinitModuleScriptOptions, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 3d46f7694c798..d8ab5222eb58e 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -64,7 +64,6 @@ const ReactNoopFlightServer = ReactFlightServer({ ) { return saveModule(reference.value); }, - prepareHostDispatcher() {}, }); type Options = { diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js index 4252195f81c1e..7d739d3178836 100644 --- a/packages/react-noop-renderer/src/ReactNoopServer.js +++ b/packages/react-noop-renderer/src/ReactNoopServer.js @@ -261,8 +261,6 @@ const ReactNoopServer = ReactFizzServer({ boundary.status = 'client-render'; }, - prepareHostDispatcher() {}, - writePreamble() {}, writeHoistables() {}, writeHoistablesForBoundary() {}, diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js index 99a8c79a62a85..9ff119ac1419d 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js @@ -47,19 +47,19 @@ describe('ReactFlightDOM', () => { JSDOM = require('jsdom').JSDOM; // Simulate the condition resolution + jest.mock('react', () => require('react/react.react-server')); + FlightReact = require('react'); + FlightReactDOM = require('react-dom'); + jest.mock('react-server-dom-webpack/server', () => require('react-server-dom-webpack/server.node.unbundled'), ); - jest.mock('react', () => require('react/react.react-server')); - const WebpackMock = require('./utils/WebpackMock'); clientExports = WebpackMock.clientExports; clientModuleError = WebpackMock.clientModuleError; webpackMap = WebpackMock.webpackMap; ReactServerDOMServer = require('react-server-dom-webpack/server'); - FlightReact = require('react'); - FlightReactDOM = require('react-dom'); // This reset is to load modules for the SSR/Browser scope. jest.unmock('react-server-dom-webpack/server'); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index f5070711534ae..1f5034f4bf985 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -36,19 +36,19 @@ describe('ReactFlightDOMBrowser', () => { jest.resetModules(); // Simulate the condition resolution + jest.mock('react', () => require('react/react.react-server')); + ReactServer = require('react'); + ReactServerDOM = require('react-dom'); + jest.mock('react-server-dom-webpack/server', () => require('react-server-dom-webpack/server.browser'), ); - const WebpackMock = require('./utils/WebpackMock'); clientExports = WebpackMock.clientExports; serverExports = WebpackMock.serverExports; webpackMap = WebpackMock.webpackMap; webpackServerMap = WebpackMock.webpackServerMap; - - ReactServer = require('react'); - ReactServerDOM = require('react-dom'); ReactServerDOMServer = require('react-server-dom-webpack/server.browser'); __unmockReact(); @@ -1172,7 +1172,19 @@ describe('ReactFlightDOMBrowser', () => { root.render(); }); expect(document.head.innerHTML).toBe( - '', + // Currently the react-dom entrypoint loads the fiber implementation + // even if you never pull in the the client APIs. this causes the fiber + // dispatcher to be present even for Flight ReactDOM calls. This is not what + // you would have in a real application but given we're runnign flight and + // fiber the in the same scope it's unavoidable until we make the entrypoint + // not automatically pull in the fiber implementation. This test currently + // asserts this be demonstrating that the preload call after the await point + // is written to the document before the call before it. We still demonstrate that + // flight handled the sync call because if the fiber implementation did it would appear + // before the after call. In the future we will change this assertion once the fiber + // implementation no long automatically gets pulled in + '', + // '', ); expect(container.innerHTML).toBe('

hello world

'); }); diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index e17b58826fb83..7085a54a4c52b 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -71,7 +71,6 @@ import { writePostamble, hoistHoistables, createHoistableState, - prepareHostDispatcher, supportsRequestStorage, requestStorage, pushFormStateMarkerIsMatching, @@ -377,7 +376,6 @@ export function createRequest( onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void), formState: void | null | ReactFormState, ): Request { - prepareHostDispatcher(); const pingedTasks: Array = []; const abortSet: Set = new Set(); const request: Request = { @@ -490,7 +488,6 @@ export function resumeRequest( onFatalError: void | ((error: mixed) => void), onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void), ): Request { - prepareHostDispatcher(); const pingedTasks: Array = []; const abortSet: Set = new Set(); const request: Request = { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 5f18f46d7ffe9..02533c7ca305b 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -69,7 +69,6 @@ import { isServerReference, supportsRequestStorage, requestStorage, - prepareHostDispatcher, createHints, initAsyncDebugInfo, } from './ReactFlightServerConfig'; @@ -345,7 +344,6 @@ export function createRequest( 'Currently React only supports one RSC renderer at a time.', ); } - prepareHostDispatcher(); ReactCurrentCache.current = DefaultCacheDispatcher; const abortSet: Set = new Set(); diff --git a/packages/react-server/src/ReactFlightServerConfigBundlerCustom.js b/packages/react-server/src/ReactFlightServerConfigBundlerCustom.js index b1fbc93ee61b1..e11c154d05f32 100644 --- a/packages/react-server/src/ReactFlightServerConfigBundlerCustom.js +++ b/packages/react-server/src/ReactFlightServerConfigBundlerCustom.js @@ -23,4 +23,3 @@ export const resolveClientReferenceMetadata = export const getServerReferenceId = $$$config.getServerReferenceId; export const getServerReferenceBoundArguments = $$$config.getServerReferenceBoundArguments; -export const prepareHostDispatcher = $$$config.prepareHostDispatcher; diff --git a/packages/react-server/src/forks/ReactFizzConfig.custom.js b/packages/react-server/src/forks/ReactFizzConfig.custom.js index 27214b525b8cb..cf1e05a54165d 100644 --- a/packages/react-server/src/forks/ReactFizzConfig.custom.js +++ b/packages/react-server/src/forks/ReactFizzConfig.custom.js @@ -78,7 +78,6 @@ export const writeCompletedBoundaryInstruction = $$$config.writeCompletedBoundaryInstruction; export const writeClientRenderBoundaryInstruction = $$$config.writeClientRenderBoundaryInstruction; -export const prepareHostDispatcher = $$$config.prepareHostDispatcher; export const NotPendingTransition = $$$config.NotPendingTransition; // ------------------------- diff --git a/packages/react-server/src/forks/ReactFlightServerConfig.custom.js b/packages/react-server/src/forks/ReactFlightServerConfig.custom.js index 953f83bf4a4f1..b78f9e3816f37 100644 --- a/packages/react-server/src/forks/ReactFlightServerConfig.custom.js +++ b/packages/react-server/src/forks/ReactFlightServerConfig.custom.js @@ -20,8 +20,6 @@ export type HintModel = any; export const isPrimaryRenderer = false; -export const prepareHostDispatcher = () => {}; - export const supportsRequestStorage = false; export const requestStorage: AsyncLocalStorage = (null: any); diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index 173ff58379251..10a6324b23bb1 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -93,7 +93,8 @@ const forks = Object.freeze({ if ( entry === 'react-dom' || entry === 'react-dom/server-rendering-stub' || - entry === 'react-dom/src/ReactDOMServer.js' + entry === 'react-dom/src/ReactDOMServer.js' || + entry === 'react-dom/unstable_testing' ) { return './packages/react-dom/src/ReactDOMSharedInternals.js'; }