Skip to content

Commit 21e4fa9

Browse files
committed
Moves the legacy implementation of flushSync to the fb entrypoint
1 parent 2b2b954 commit 21e4fa9

File tree

5 files changed

+245
-30
lines changed

5 files changed

+245
-30
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export {
3535
preinit,
3636
preinitModule,
3737
version,
38-
} from './src/client/ReactDOM';
38+
} from './src/client/ReactDOMFB';
3939

4040
export {
4141
createRoot,

packages/react-dom/index.experimental.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export {
1212
createPortal,
1313
createRoot,
1414
hydrateRoot,
15+
flushSync,
1516
unstable_batchedUpdates,
1617
unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority.
1718
useFormStatus,
@@ -24,5 +25,3 @@ export {
2425
preinitModule,
2526
version,
2627
} from './src/client/ReactDOM';
27-
28-
export {flushSync} from './src/shared/ReactDOMFlushSync';

packages/react-dom/src/client/ReactDOM.js

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,15 @@ import {
2424
unstable_renderSubtreeIntoContainer,
2525
unmountComponentAtNode,
2626
} from './ReactDOMLegacy';
27+
import {flushSync} from '../shared/ReactDOMFlushSync';
2728
import {
2829
createRoot as createRootImpl,
2930
hydrateRoot as hydrateRootImpl,
3031
isValidContainer,
3132
} from './ReactDOMRoot';
3233
import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';
3334

34-
import {
35-
flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering,
36-
isAlreadyRendering,
37-
injectIntoDevTools,
38-
} from 'react-reconciler/src/ReactFiberReconciler';
35+
import {injectIntoDevTools} from 'react-reconciler/src/ReactFiberReconciler';
3936
import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
4037
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
4138
import {canUseDOM} from 'shared/ExecutionEnvironment';
@@ -144,25 +141,6 @@ function hydrateRoot(
144141
return hydrateRootImpl(container, initialChildren, options);
145142
}
146143

147-
// Overload the definition to the two valid signatures.
148-
// Warning, this opts-out of checking the function body.
149-
declare function flushSyncFromReconciler<R>(fn: () => R): R;
150-
// eslint-disable-next-line no-redeclare
151-
declare function flushSyncFromReconciler(): void;
152-
// eslint-disable-next-line no-redeclare
153-
function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {
154-
if (__DEV__) {
155-
if (isAlreadyRendering()) {
156-
console.error(
157-
'flushSync was called from inside a lifecycle method. React cannot ' +
158-
'flush when React is already rendering. Consider moving this call to ' +
159-
'a scheduler task or micro task.',
160-
);
161-
}
162-
}
163-
return flushSyncWithoutWarningIfAlreadyRendering(fn);
164-
}
165-
166144
// Expose findDOMNode on internals
167145
Internals.findDOMNode = findDOMNode;
168146

@@ -176,7 +154,7 @@ function unstable_batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
176154
export {
177155
createPortal,
178156
unstable_batchedUpdates,
179-
flushSyncFromReconciler as flushSync,
157+
flushSync,
180158
ReactVersion as version,
181159
// Disabled behind disableLegacyReactDOMAPIs
182160
findDOMNode,
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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 type {ReactNodeList} from 'shared/ReactTypes';
11+
import type {
12+
Container,
13+
PublicInstance,
14+
} from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
15+
import type {
16+
RootType,
17+
HydrateRootOptions,
18+
CreateRootOptions,
19+
} from './ReactDOMRoot';
20+
21+
import {
22+
findDOMNode,
23+
render,
24+
unstable_renderSubtreeIntoContainer,
25+
unmountComponentAtNode,
26+
} from './ReactDOMLegacy';
27+
import {
28+
createRoot as createRootImpl,
29+
hydrateRoot as hydrateRootImpl,
30+
isValidContainer,
31+
} from './ReactDOMRoot';
32+
import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';
33+
34+
import {
35+
flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering,
36+
isAlreadyRendering,
37+
injectIntoDevTools,
38+
} from 'react-reconciler/src/ReactFiberReconciler';
39+
import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
40+
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
41+
import {canUseDOM} from 'shared/ExecutionEnvironment';
42+
import ReactVersion from 'shared/ReactVersion';
43+
44+
import {
45+
getClosestInstanceFromNode,
46+
getInstanceFromNode,
47+
getNodeFromInstance,
48+
getFiberCurrentPropsFromNode,
49+
} from 'react-dom-bindings/src/client/ReactDOMComponentTree';
50+
import {
51+
enqueueStateRestore,
52+
restoreStateIfNeeded,
53+
} from 'react-dom-bindings/src/events/ReactDOMControlledComponent';
54+
import Internals from '../ReactDOMSharedInternals';
55+
56+
export {
57+
prefetchDNS,
58+
preconnect,
59+
preload,
60+
preloadModule,
61+
preinit,
62+
preinitModule,
63+
} from '../shared/ReactDOMFloat';
64+
export {
65+
useFormStatus,
66+
useFormState,
67+
} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
68+
69+
if (__DEV__) {
70+
if (
71+
typeof Map !== 'function' ||
72+
// $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype
73+
Map.prototype == null ||
74+
typeof Map.prototype.forEach !== 'function' ||
75+
typeof Set !== 'function' ||
76+
// $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype
77+
Set.prototype == null ||
78+
typeof Set.prototype.clear !== 'function' ||
79+
typeof Set.prototype.forEach !== 'function'
80+
) {
81+
console.error(
82+
'React depends on Map and Set built-in types. Make sure that you load a ' +
83+
'polyfill in older browsers. https://react.dev/link/react-polyfills',
84+
);
85+
}
86+
}
87+
88+
function createPortal(
89+
children: ReactNodeList,
90+
container: Element | DocumentFragment,
91+
key: ?string = null,
92+
): React$Portal {
93+
if (!isValidContainer(container)) {
94+
throw new Error('Target container is not a DOM element.');
95+
}
96+
97+
// TODO: pass ReactDOM portal implementation as third argument
98+
// $FlowFixMe[incompatible-return] The Flow type is opaque but there's no way to actually create it.
99+
return createPortalImpl(children, container, null, key);
100+
}
101+
102+
function renderSubtreeIntoContainer(
103+
parentComponent: React$Component<any, any>,
104+
element: React$Element<any>,
105+
containerNode: Container,
106+
callback: ?Function,
107+
): React$Component<any, any> | PublicInstance | null {
108+
return unstable_renderSubtreeIntoContainer(
109+
parentComponent,
110+
element,
111+
containerNode,
112+
callback,
113+
);
114+
}
115+
116+
function createRoot(
117+
container: Element | Document | DocumentFragment,
118+
options?: CreateRootOptions,
119+
): RootType {
120+
if (__DEV__) {
121+
if (!Internals.usingClientEntryPoint && !__UMD__) {
122+
console.error(
123+
'You are importing createRoot from "react-dom" which is not supported. ' +
124+
'You should instead import it from "react-dom/client".',
125+
);
126+
}
127+
}
128+
return createRootImpl(container, options);
129+
}
130+
131+
function hydrateRoot(
132+
container: Document | Element,
133+
initialChildren: ReactNodeList,
134+
options?: HydrateRootOptions,
135+
): RootType {
136+
if (__DEV__) {
137+
if (!Internals.usingClientEntryPoint && !__UMD__) {
138+
console.error(
139+
'You are importing hydrateRoot from "react-dom" which is not supported. ' +
140+
'You should instead import it from "react-dom/client".',
141+
);
142+
}
143+
}
144+
return hydrateRootImpl(container, initialChildren, options);
145+
}
146+
147+
// Overload the definition to the two valid signatures.
148+
// Warning, this opts-out of checking the function body.
149+
declare function flushSync<R>(fn: () => R): R;
150+
// eslint-disable-next-line no-redeclare
151+
declare function flushSync(): void;
152+
// eslint-disable-next-line no-redeclare
153+
function flushSync<R>(fn: (() => R) | void): R | void {
154+
if (__DEV__) {
155+
if (isAlreadyRendering()) {
156+
console.error(
157+
'flushSync was called from inside a lifecycle method. React cannot ' +
158+
'flush when React is already rendering. Consider moving this call to ' +
159+
'a scheduler task or micro task.',
160+
);
161+
}
162+
}
163+
return flushSyncWithoutWarningIfAlreadyRendering(fn);
164+
}
165+
166+
// Expose findDOMNode on internals
167+
Internals.findDOMNode = findDOMNode;
168+
169+
function unstable_batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
170+
// batchedUpdates was a legacy mode feature that is a no-op outside of
171+
// legacy mode. In 19, we made it an actual no-op, but we're keeping it
172+
// for now since there may be libraries that still include it.
173+
return fn(a);
174+
}
175+
176+
export {
177+
createPortal,
178+
unstable_batchedUpdates,
179+
flushSync,
180+
ReactVersion as version,
181+
// Disabled behind disableLegacyReactDOMAPIs
182+
findDOMNode,
183+
render,
184+
unmountComponentAtNode,
185+
// exposeConcurrentModeAPIs
186+
createRoot,
187+
hydrateRoot,
188+
// Disabled behind disableUnstableRenderSubtreeIntoContainer
189+
renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer,
190+
// enableCreateEventHandleAPI
191+
createEventHandle as unstable_createEventHandle,
192+
// TODO: Remove this once callers migrate to alternatives.
193+
// This should only be used by React internals.
194+
runWithPriority as unstable_runWithPriority,
195+
};
196+
197+
// Keep in sync with ReactTestUtils.js.
198+
// This is an array for better minification.
199+
Internals.Events = [
200+
getInstanceFromNode,
201+
getNodeFromInstance,
202+
getFiberCurrentPropsFromNode,
203+
enqueueStateRestore,
204+
restoreStateIfNeeded,
205+
unstable_batchedUpdates,
206+
];
207+
208+
const foundDevTools = injectIntoDevTools({
209+
findFiberByHostInstance: getClosestInstanceFromNode,
210+
bundleType: __DEV__ ? 1 : 0,
211+
version: ReactVersion,
212+
rendererPackageName: 'react-dom',
213+
});
214+
215+
if (__DEV__) {
216+
if (!foundDevTools && canUseDOM && window.top === window.self) {
217+
// If we're in Chrome or Firefox, provide a download link if not installed.
218+
if (
219+
(navigator.userAgent.indexOf('Chrome') > -1 &&
220+
navigator.userAgent.indexOf('Edge') === -1) ||
221+
navigator.userAgent.indexOf('Firefox') > -1
222+
) {
223+
const protocol = window.location.protocol;
224+
// Don't warn in exotic cases like chrome-extension://.
225+
if (/^(https?|file):$/.test(protocol)) {
226+
// eslint-disable-next-line react-internal/no-production-logging
227+
console.info(
228+
'%cDownload the React DevTools ' +
229+
'for a better development experience: ' +
230+
'https://react.dev/link/react-devtools' +
231+
(protocol === 'file:'
232+
? '\nYou might need to use a local HTTP server (instead of file://): ' +
233+
'https://react.dev/link/react-devtools-faq'
234+
: ''),
235+
'font-weight:bold',
236+
);
237+
}
238+
}
239+
}
240+
}

packages/react-dom/src/client/ReactDOMRoot.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ import {
1919
allowConcurrentByDefault,
2020
disableCommentsAsDOMContainers,
2121
enableAsyncActions,
22-
disableLegacyMode,
2322
} from 'shared/ReactFeatureFlags';
24-
import {flushSync} from '../shared/ReactDOMFlushSync';
2523

2624
export type RootType = {
2725
render(children: ReactNodeList): void,

0 commit comments

Comments
 (0)