Skip to content

Commit

Permalink
Move client only exports to react-dom/client
Browse files Browse the repository at this point in the history
This change updates the entrypoints for `react-dom` to only include exports which make sense in every runtime (Flight, Fizz, and Fiber). The main benefit to doing this is we stop including the entire client build when importing anything from `react-dom`. The server-rendering-stub was added as a manual way of doing this prior to the next major and now that stub simply reexports from `react-dom`. In a future major we will remove the stub altogether.

This change affects the OSS channels but does not update how the meta entrypoints are organized
  • Loading branch information
gnoff committed Feb 7, 2024
1 parent cd81c4a commit f095713
Show file tree
Hide file tree
Showing 30 changed files with 286 additions and 357 deletions.
42 changes: 42 additions & 0 deletions packages/react-dom-bindings/src/client/ReactDOMContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {disableCommentsAsDOMContainers} from 'shared/ReactFeatureFlags';

import {
ELEMENT_NODE,
COMMENT_NODE,
DOCUMENT_NODE,
DOCUMENT_FRAGMENT_NODE,
} from './HTMLNodeType';

export function isValidContainer(node: any): boolean {
return !!(
node &&
(node.nodeType === ELEMENT_NODE ||
node.nodeType === DOCUMENT_NODE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
(!disableCommentsAsDOMContainers &&
node.nodeType === COMMENT_NODE &&
(node: any).nodeValue === ' react-mount-point-unstable '))
);
}

// TODO: Remove this function which also includes comment nodes.
// We only use it in places that are currently more relaxed.
export function isValidContainerLegacy(node: any): boolean {
return !!(
node &&
(node.nodeType === ELEMENT_NODE ||
node.nodeType === DOCUMENT_NODE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
(node.nodeType === COMMENT_NODE &&
(node: any).nodeValue === ' react-mount-point-unstable '))
);
}
56 changes: 43 additions & 13 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -20,6 +19,7 @@ import type {ReactScopeInstance} from 'shared/ReactTypes';
import type {AncestorInfoDev} from './validateDOMNesting';
import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
import type {
HostDispatcher,
CrossOriginEnum,
PreloadImplOptions,
PreloadModuleImplOptions,
Expand All @@ -28,6 +28,10 @@ import type {
PreinitModuleScriptOptions,
} from 'react-dom/src/shared/ReactDOMTypes';

import {
isAlreadyRendering,
flushSync as flushSyncWithoutWarningIfAlreadyRendering,
} from 'react-reconciler/src/ReactFiberReconciler';
import {NotPending} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
Expand Down Expand Up @@ -106,6 +110,26 @@ import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';
import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';

const ReactDOMClientDispatcher: HostDispatcher = {
prefetchDNS,
preconnect,
preload,
preloadModule,
preinitStyle,
preinitScript,
preinitModuleScript,
flushSync,
nextDispatcher: null,
};

import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
if (ReactDOMCurrentDispatcher.current) {
ReactDOMCurrentDispatcher.current.nextDispatcher = ReactDOMClientDispatcher;
} else {
ReactDOMCurrentDispatcher.current = ReactDOMClientDispatcher;
}

export type Type = string;
export type Props = {
autoFocus?: boolean,
Expand Down Expand Up @@ -2083,18 +2107,24 @@ 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 = {
prefetchDNS,
preconnect,
preload,
preloadModule,
preinitStyle,
preinitScript,
preinitModuleScript,
};
function flushSync<R>(fn: void | (() => R)): void | R {
if (__DEV__) {
if (isAlreadyRendering()) {
console.error(
'flushSync was called from inside a lifecycle method. React cannot ' +
'flush when React is already rendering. Consider moving this call to ' +
'a scheduler task or micro task.',
);
}
}
if (ReactDOMClientDispatcher.nextDispatcher) {
return ReactDOMClientDispatcher.nextDispatcher.flushSync(() =>
flushSyncWithoutWarningIfAlreadyRendering(fn),
);
} else {
return flushSyncWithoutWarningIfAlreadyRendering(fn);
}
}

// We expect this to get inlined. It is a function mostly to communicate the special nature of
// how we resolve the HoistableRoot for ReactDOM.pre*() methods. Because we support calling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,22 @@ export const ReactDOMFlightServerDispatcher: HostDispatcher = {
preinitStyle,
preinitScript,
preinitModuleScript,
flushSync,
nextDispatcher: null,
};

function flushSync<R>(fn: void | (() => R)): void | R {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
return ReactDOMFlightServerDispatcher.nextDispatcher.flushSync(fn);
} else if (fn) {
return fn();
}
}

function prefetchDNS(href: string) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.prefetchDNS(href);
}
if (enableFloat) {
if (typeof href === 'string' && href) {
const request = resolveRequest();
Expand All @@ -54,6 +67,9 @@ function prefetchDNS(href: string) {
}

function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preconnect(href, crossOrigin);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand All @@ -77,6 +93,9 @@ function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
}

function preload(href: string, as: string, options?: ?PreloadImplOptions) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preload(href, as, options);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand Down Expand Up @@ -110,6 +129,9 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
}

function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preloadModule(href, options);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand Down Expand Up @@ -138,6 +160,13 @@ function preinitStyle(
precedence: ?string,
options?: ?PreinitStyleOptions,
) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preinitStyle(
href,
precedence,
options,
);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand Down Expand Up @@ -168,6 +197,9 @@ function preinitStyle(
}

function preinitScript(href: string, options?: ?PreinitScriptOptions) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preinitScript(href, options);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand Down Expand Up @@ -195,6 +227,12 @@ function preinitModuleScript(
href: string,
options?: ?PreinitModuleScriptOptions,
) {
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
ReactDOMFlightServerDispatcher.nextDispatcher.preinitModuleScript(
href,
options,
);
}
if (enableFloat) {
if (typeof href === 'string') {
const request = resolveRequest();
Expand Down
23 changes: 17 additions & 6 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import type {ReactNodeList, ReactCustomFormAction} from 'shared/ReactTypes';
import type {
HostDispatcher,
CrossOriginEnum,
PreloadImplOptions,
PreloadModuleImplOptions,
Expand Down Expand Up @@ -86,23 +87,33 @@ import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMRes

import {NotPending} from '../shared/ReactDOMFormActions';

import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;

const ReactDOMServerDispatcher = {
const ReactDOMServerDispatcher: HostDispatcher = {
prefetchDNS,
preconnect,
preload,
preloadModule,
preinitStyle,
preinitScript,
preinitModuleScript,
flushSync,
nextDispatcher: null,
};

export function prepareHostDispatcher() {
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
if (ReactDOMCurrentDispatcher.current) {
ReactDOMCurrentDispatcher.current.nextDispatcher = ReactDOMServerDispatcher;
} else {
ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher;
}

function flushSync<R>(fn: void | (() => R)): void | R {
if (ReactDOMServerDispatcher.nextDispatcher) {
return ReactDOMServerDispatcher.nextDispatcher.flushSync(fn);
} else if (fn) {
return fn();
}
}

// 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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ export {
writeHoistables,
writePostamble,
hoistHoistables,
prepareHostDispatcher,
resetResumableState,
completeResumableState,
emitEarlyPreloads,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import type {
PreinitModuleScriptOptions,
} from 'react-dom/src/shared/ReactDOMTypes';

import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher';
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;

import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher';

export function prepareHostDispatcher(): void {
if (ReactDOMCurrentDispatcher.current) {
ReactDOMCurrentDispatcher.current.nextDispatcher =
ReactDOMFlightServerDispatcher;
} else {
ReactDOMCurrentDispatcher.current = ReactDOMFlightServerDispatcher;
}

Expand Down
51 changes: 6 additions & 45 deletions packages/react-dom/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,9 @@

'use strict';

import type {ReactNodeList} from 'shared/ReactTypes';
import type {
RootType,
HydrateRootOptions,
CreateRootOptions,
} from './src/client/ReactDOMRoot';

import {
createRoot as createRootImpl,
hydrateRoot as hydrateRootImpl,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as Internals,
} from './';

export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
if (__DEV__) {
Internals.usingClientEntryPoint = true;
}
try {
return createRootImpl(container, options);
} finally {
if (__DEV__) {
Internals.usingClientEntryPoint = false;
}
}
}

export function hydrateRoot(
container: Document | Element,
children: ReactNodeList,
options?: HydrateRootOptions,
): RootType {
if (__DEV__) {
Internals.usingClientEntryPoint = true;
}
try {
return hydrateRootImpl(container, children, options);
} finally {
if (__DEV__) {
Internals.usingClientEntryPoint = false;
}
}
}
export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals';
export {
createRoot,
hydrateRoot,
unstable_createEventHandle,
} from './src/client/ReactDOMClient';
14 changes: 8 additions & 6 deletions packages/react-dom/index.classic.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ Object.assign((Internals: any), {
},
});

export {Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED};
export {
createPortal,
createRoot,
hydrateRoot,
findDOMNode,
flushSync,
hydrate,
render,
unmountComponentAtNode,
unstable_batchedUpdates,
unstable_createEventHandle,
unstable_renderSubtreeIntoContainer,
unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority.
} from './src/client/ReactDOMClient';

export {
createPortal,
flushSync,
unstable_batchedUpdates,
useFormStatus,
useFormState,
prefetchDNS,
Expand All @@ -40,6 +44,4 @@ export {
preinit,
preinitModule,
version,
} from './src/client/ReactDOM';

export {Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED};
} from './src/shared/ReactDOM';
Loading

0 comments on commit f095713

Please sign in to comment.