Skip to content

Commit f095713

Browse files
committed
Move client only exports to react-dom/client
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
1 parent cd81c4a commit f095713

30 files changed

+286
-357
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import {disableCommentsAsDOMContainers} from 'shared/ReactFeatureFlags';
11+
12+
import {
13+
ELEMENT_NODE,
14+
COMMENT_NODE,
15+
DOCUMENT_NODE,
16+
DOCUMENT_FRAGMENT_NODE,
17+
} from './HTMLNodeType';
18+
19+
export function isValidContainer(node: any): boolean {
20+
return !!(
21+
node &&
22+
(node.nodeType === ELEMENT_NODE ||
23+
node.nodeType === DOCUMENT_NODE ||
24+
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
25+
(!disableCommentsAsDOMContainers &&
26+
node.nodeType === COMMENT_NODE &&
27+
(node: any).nodeValue === ' react-mount-point-unstable '))
28+
);
29+
}
30+
31+
// TODO: Remove this function which also includes comment nodes.
32+
// We only use it in places that are currently more relaxed.
33+
export function isValidContainerLegacy(node: any): boolean {
34+
return !!(
35+
node &&
36+
(node.nodeType === ELEMENT_NODE ||
37+
node.nodeType === DOCUMENT_NODE ||
38+
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
39+
(node.nodeType === COMMENT_NODE &&
40+
(node: any).nodeValue === ' react-mount-point-unstable '))
41+
);
42+
}

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* @flow
88
*/
99

10-
import type {HostDispatcher} from 'react-dom/src/shared/ReactDOMTypes';
1110
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
1211
import type {DOMEventName} from '../events/DOMEventNames';
1312
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
@@ -20,6 +19,7 @@ import type {ReactScopeInstance} from 'shared/ReactTypes';
2019
import type {AncestorInfoDev} from './validateDOMNesting';
2120
import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
2221
import type {
22+
HostDispatcher,
2323
CrossOriginEnum,
2424
PreloadImplOptions,
2525
PreloadModuleImplOptions,
@@ -28,6 +28,10 @@ import type {
2828
PreinitModuleScriptOptions,
2929
} from 'react-dom/src/shared/ReactDOMTypes';
3030

31+
import {
32+
isAlreadyRendering,
33+
flushSync as flushSyncWithoutWarningIfAlreadyRendering,
34+
} from 'react-reconciler/src/ReactFiberReconciler';
3135
import {NotPending} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
3236
import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
3337
import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
@@ -106,6 +110,26 @@ import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
106110
import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';
107111
import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';
108112

113+
const ReactDOMClientDispatcher: HostDispatcher = {
114+
prefetchDNS,
115+
preconnect,
116+
preload,
117+
preloadModule,
118+
preinitStyle,
119+
preinitScript,
120+
preinitModuleScript,
121+
flushSync,
122+
nextDispatcher: null,
123+
};
124+
125+
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
126+
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
127+
if (ReactDOMCurrentDispatcher.current) {
128+
ReactDOMCurrentDispatcher.current.nextDispatcher = ReactDOMClientDispatcher;
129+
} else {
130+
ReactDOMCurrentDispatcher.current = ReactDOMClientDispatcher;
131+
}
132+
109133
export type Type = string;
110134
export type Props = {
111135
autoFocus?: boolean,
@@ -2083,18 +2107,24 @@ function getDocumentFromRoot(root: HoistableRoot): Document {
20832107
return root.ownerDocument || root;
20842108
}
20852109

2086-
// We want this to be the default dispatcher on ReactDOMSharedInternals but we don't want to mutate
2087-
// internals in Module scope. Instead we export it and Internals will import it. There is already a cycle
2088-
// from Internals -> ReactDOM -> HostConfig -> Internals so this doesn't introduce a new one.
2089-
export const ReactDOMClientDispatcher: HostDispatcher = {
2090-
prefetchDNS,
2091-
preconnect,
2092-
preload,
2093-
preloadModule,
2094-
preinitStyle,
2095-
preinitScript,
2096-
preinitModuleScript,
2097-
};
2110+
function flushSync<R>(fn: void | (() => R)): void | R {
2111+
if (__DEV__) {
2112+
if (isAlreadyRendering()) {
2113+
console.error(
2114+
'flushSync was called from inside a lifecycle method. React cannot ' +
2115+
'flush when React is already rendering. Consider moving this call to ' +
2116+
'a scheduler task or micro task.',
2117+
);
2118+
}
2119+
}
2120+
if (ReactDOMClientDispatcher.nextDispatcher) {
2121+
return ReactDOMClientDispatcher.nextDispatcher.flushSync(() =>
2122+
flushSyncWithoutWarningIfAlreadyRendering(fn),
2123+
);
2124+
} else {
2125+
return flushSyncWithoutWarningIfAlreadyRendering(fn);
2126+
}
2127+
}
20982128

20992129
// We expect this to get inlined. It is a function mostly to communicate the special nature of
21002130
// how we resolve the HoistableRoot for ReactDOM.pre*() methods. Because we support calling

packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,22 @@ export const ReactDOMFlightServerDispatcher: HostDispatcher = {
3333
preinitStyle,
3434
preinitScript,
3535
preinitModuleScript,
36+
flushSync,
37+
nextDispatcher: null,
3638
};
3739

40+
function flushSync<R>(fn: void | (() => R)): void | R {
41+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
42+
return ReactDOMFlightServerDispatcher.nextDispatcher.flushSync(fn);
43+
} else if (fn) {
44+
return fn();
45+
}
46+
}
47+
3848
function prefetchDNS(href: string) {
49+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
50+
ReactDOMFlightServerDispatcher.nextDispatcher.prefetchDNS(href);
51+
}
3952
if (enableFloat) {
4053
if (typeof href === 'string' && href) {
4154
const request = resolveRequest();
@@ -54,6 +67,9 @@ function prefetchDNS(href: string) {
5467
}
5568

5669
function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
70+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
71+
ReactDOMFlightServerDispatcher.nextDispatcher.preconnect(href, crossOrigin);
72+
}
5773
if (enableFloat) {
5874
if (typeof href === 'string') {
5975
const request = resolveRequest();
@@ -77,6 +93,9 @@ function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
7793
}
7894

7995
function preload(href: string, as: string, options?: ?PreloadImplOptions) {
96+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
97+
ReactDOMFlightServerDispatcher.nextDispatcher.preload(href, as, options);
98+
}
8099
if (enableFloat) {
81100
if (typeof href === 'string') {
82101
const request = resolveRequest();
@@ -110,6 +129,9 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
110129
}
111130

112131
function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
132+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
133+
ReactDOMFlightServerDispatcher.nextDispatcher.preloadModule(href, options);
134+
}
113135
if (enableFloat) {
114136
if (typeof href === 'string') {
115137
const request = resolveRequest();
@@ -138,6 +160,13 @@ function preinitStyle(
138160
precedence: ?string,
139161
options?: ?PreinitStyleOptions,
140162
) {
163+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
164+
ReactDOMFlightServerDispatcher.nextDispatcher.preinitStyle(
165+
href,
166+
precedence,
167+
options,
168+
);
169+
}
141170
if (enableFloat) {
142171
if (typeof href === 'string') {
143172
const request = resolveRequest();
@@ -168,6 +197,9 @@ function preinitStyle(
168197
}
169198

170199
function preinitScript(href: string, options?: ?PreinitScriptOptions) {
200+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
201+
ReactDOMFlightServerDispatcher.nextDispatcher.preinitScript(href, options);
202+
}
171203
if (enableFloat) {
172204
if (typeof href === 'string') {
173205
const request = resolveRequest();
@@ -195,6 +227,12 @@ function preinitModuleScript(
195227
href: string,
196228
options?: ?PreinitModuleScriptOptions,
197229
) {
230+
if (ReactDOMFlightServerDispatcher.nextDispatcher) {
231+
ReactDOMFlightServerDispatcher.nextDispatcher.preinitModuleScript(
232+
href,
233+
options,
234+
);
235+
}
198236
if (enableFloat) {
199237
if (typeof href === 'string') {
200238
const request = resolveRequest();

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import type {ReactNodeList, ReactCustomFormAction} from 'shared/ReactTypes';
1111
import type {
12+
HostDispatcher,
1213
CrossOriginEnum,
1314
PreloadImplOptions,
1415
PreloadModuleImplOptions,
@@ -86,23 +87,33 @@ import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMRes
8687

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

89-
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
90-
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
91-
92-
const ReactDOMServerDispatcher = {
90+
const ReactDOMServerDispatcher: HostDispatcher = {
9391
prefetchDNS,
9492
preconnect,
9593
preload,
9694
preloadModule,
9795
preinitStyle,
9896
preinitScript,
9997
preinitModuleScript,
98+
flushSync,
99+
nextDispatcher: null,
100100
};
101-
102-
export function prepareHostDispatcher() {
101+
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
102+
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
103+
if (ReactDOMCurrentDispatcher.current) {
104+
ReactDOMCurrentDispatcher.current.nextDispatcher = ReactDOMServerDispatcher;
105+
} else {
103106
ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher;
104107
}
105108

109+
function flushSync<R>(fn: void | (() => R)): void | R {
110+
if (ReactDOMServerDispatcher.nextDispatcher) {
111+
return ReactDOMServerDispatcher.nextDispatcher.flushSync(fn);
112+
} else if (fn) {
113+
return fn();
114+
}
115+
}
116+
106117
// We make every property of the descriptor optional because it is not a contract that
107118
// the headers provided by onHeaders has any particular header types.
108119
export type HeadersDescriptor = {

packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ export {
163163
writeHoistables,
164164
writePostamble,
165165
hoistHoistables,
166-
prepareHostDispatcher,
167166
resetResumableState,
168167
completeResumableState,
169168
emitEarlyPreloads,

packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ import type {
1616
PreinitModuleScriptOptions,
1717
} from 'react-dom/src/shared/ReactDOMTypes';
1818

19+
import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher';
1920
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
2021
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
21-
22-
import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher';
23-
24-
export function prepareHostDispatcher(): void {
22+
if (ReactDOMCurrentDispatcher.current) {
23+
ReactDOMCurrentDispatcher.current.nextDispatcher =
24+
ReactDOMFlightServerDispatcher;
25+
} else {
2526
ReactDOMCurrentDispatcher.current = ReactDOMFlightServerDispatcher;
2627
}
2728

packages/react-dom/client.js

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,9 @@
99

1010
'use strict';
1111

12-
import type {ReactNodeList} from 'shared/ReactTypes';
13-
import type {
14-
RootType,
15-
HydrateRootOptions,
16-
CreateRootOptions,
17-
} from './src/client/ReactDOMRoot';
18-
19-
import {
20-
createRoot as createRootImpl,
21-
hydrateRoot as hydrateRootImpl,
22-
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as Internals,
23-
} from './';
24-
25-
export function createRoot(
26-
container: Element | Document | DocumentFragment,
27-
options?: CreateRootOptions,
28-
): RootType {
29-
if (__DEV__) {
30-
Internals.usingClientEntryPoint = true;
31-
}
32-
try {
33-
return createRootImpl(container, options);
34-
} finally {
35-
if (__DEV__) {
36-
Internals.usingClientEntryPoint = false;
37-
}
38-
}
39-
}
40-
41-
export function hydrateRoot(
42-
container: Document | Element,
43-
children: ReactNodeList,
44-
options?: HydrateRootOptions,
45-
): RootType {
46-
if (__DEV__) {
47-
Internals.usingClientEntryPoint = true;
48-
}
49-
try {
50-
return hydrateRootImpl(container, children, options);
51-
} finally {
52-
if (__DEV__) {
53-
Internals.usingClientEntryPoint = false;
54-
}
55-
}
56-
}
12+
export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals';
13+
export {
14+
createRoot,
15+
hydrateRoot,
16+
unstable_createEventHandle,
17+
} from './src/client/ReactDOMClient';

packages/react-dom/index.classic.fb.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,23 @@ Object.assign((Internals: any), {
1818
},
1919
});
2020

21+
export {Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED};
2122
export {
22-
createPortal,
2323
createRoot,
2424
hydrateRoot,
2525
findDOMNode,
26-
flushSync,
2726
hydrate,
2827
render,
2928
unmountComponentAtNode,
30-
unstable_batchedUpdates,
3129
unstable_createEventHandle,
3230
unstable_renderSubtreeIntoContainer,
3331
unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority.
32+
} from './src/client/ReactDOMClient';
33+
34+
export {
35+
createPortal,
36+
flushSync,
37+
unstable_batchedUpdates,
3438
useFormStatus,
3539
useFormState,
3640
prefetchDNS,
@@ -40,6 +44,4 @@ export {
4044
preinit,
4145
preinitModule,
4246
version,
43-
} from './src/client/ReactDOM';
44-
45-
export {Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED};
47+
} from './src/shared/ReactDOM';

0 commit comments

Comments
 (0)