Skip to content

Commit

Permalink
Moves the legacy implementation of flushSync to the fb entrypoint
Browse files Browse the repository at this point in the history
  • Loading branch information
gnoff committed Apr 1, 2024
1 parent 8918e5f commit 82e73ff
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 30 deletions.
2 changes: 1 addition & 1 deletion packages/react-dom/index.classic.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export {
preinit,
preinitModule,
version,
} from './src/client/ReactDOM';
} from './src/client/ReactDOMFB';

export {
createRoot,
Expand Down
3 changes: 1 addition & 2 deletions packages/react-dom/index.experimental.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
createPortal,
createRoot,
hydrateRoot,
flushSync,
unstable_batchedUpdates,
unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority.
useFormStatus,
Expand All @@ -24,5 +25,3 @@ export {
preinitModule,
version,
} from './src/client/ReactDOM';

export {flushSync} from './src/shared/ReactDOMFlushSync';
28 changes: 3 additions & 25 deletions packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,15 @@ import {
unstable_renderSubtreeIntoContainer,
unmountComponentAtNode,
} from './ReactDOMLegacy';
import {flushSync} from '../shared/ReactDOMFlushSync';
import {
createRoot as createRootImpl,
hydrateRoot as hydrateRootImpl,
isValidContainer,
} from './ReactDOMRoot';
import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';

import {
flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering,
isAlreadyRendering,
injectIntoDevTools,
} from 'react-reconciler/src/ReactFiberReconciler';
import {injectIntoDevTools} from 'react-reconciler/src/ReactFiberReconciler';
import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
import {canUseDOM} from 'shared/ExecutionEnvironment';
Expand Down Expand Up @@ -144,25 +141,6 @@ function hydrateRoot(
return hydrateRootImpl(container, initialChildren, options);
}

// Overload the definition to the two valid signatures.
// Warning, this opts-out of checking the function body.
declare function flushSyncFromReconciler<R>(fn: () => R): R;
// eslint-disable-next-line no-redeclare
declare function flushSyncFromReconciler(): void;
// eslint-disable-next-line no-redeclare
function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {
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.',
);
}
}
return flushSyncWithoutWarningIfAlreadyRendering(fn);
}

// Expose findDOMNode on internals
Internals.findDOMNode = findDOMNode;

Expand All @@ -176,7 +154,7 @@ function unstable_batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
export {
createPortal,
unstable_batchedUpdates,
flushSyncFromReconciler as flushSync,
flushSync,
ReactVersion as version,
// Disabled behind disableLegacyReactDOMAPIs
findDOMNode,
Expand Down
240 changes: 240 additions & 0 deletions packages/react-dom/src/client/ReactDOMFB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/**
* 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 type {ReactNodeList} from 'shared/ReactTypes';
import type {
Container,
PublicInstance,
} from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
import type {
RootType,
HydrateRootOptions,
CreateRootOptions,
} from './ReactDOMRoot';

import {
findDOMNode,
render,
unstable_renderSubtreeIntoContainer,
unmountComponentAtNode,
} from './ReactDOMLegacy';
import {
createRoot as createRootImpl,
hydrateRoot as hydrateRootImpl,
isValidContainer,
} from './ReactDOMRoot';
import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';

import {
flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering,
isAlreadyRendering,
injectIntoDevTools,
} from 'react-reconciler/src/ReactFiberReconciler';
import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
import {canUseDOM} from 'shared/ExecutionEnvironment';
import ReactVersion from 'shared/ReactVersion';

import {
getClosestInstanceFromNode,
getInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
} from 'react-dom-bindings/src/client/ReactDOMComponentTree';
import {
enqueueStateRestore,
restoreStateIfNeeded,
} from 'react-dom-bindings/src/events/ReactDOMControlledComponent';
import Internals from '../ReactDOMSharedInternals';

export {
prefetchDNS,
preconnect,
preload,
preloadModule,
preinit,
preinitModule,
} from '../shared/ReactDOMFloat';
export {
useFormStatus,
useFormState,
} from 'react-dom-bindings/src/shared/ReactDOMFormActions';

if (__DEV__) {
if (
typeof Map !== 'function' ||
// $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype
Map.prototype == null ||
typeof Map.prototype.forEach !== 'function' ||
typeof Set !== 'function' ||
// $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype
Set.prototype == null ||
typeof Set.prototype.clear !== 'function' ||
typeof Set.prototype.forEach !== 'function'
) {
console.error(
'React depends on Map and Set built-in types. Make sure that you load a ' +
'polyfill in older browsers. https://react.dev/link/react-polyfills',
);
}
}

function createPortal(
children: ReactNodeList,
container: Element | DocumentFragment,
key: ?string = null,
): React$Portal {
if (!isValidContainer(container)) {
throw new Error('Target container is not a DOM element.');
}

// TODO: pass ReactDOM portal implementation as third argument
// $FlowFixMe[incompatible-return] The Flow type is opaque but there's no way to actually create it.
return createPortalImpl(children, container, null, key);
}

function renderSubtreeIntoContainer(
parentComponent: React$Component<any, any>,
element: React$Element<any>,
containerNode: Container,
callback: ?Function,
): React$Component<any, any> | PublicInstance | null {
return unstable_renderSubtreeIntoContainer(
parentComponent,
element,
containerNode,
callback,
);
}

function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
if (__DEV__) {
if (!Internals.usingClientEntryPoint && !__UMD__) {
console.error(
'You are importing createRoot from "react-dom" which is not supported. ' +
'You should instead import it from "react-dom/client".',
);
}
}
return createRootImpl(container, options);
}

function hydrateRoot(
container: Document | Element,
initialChildren: ReactNodeList,
options?: HydrateRootOptions,
): RootType {
if (__DEV__) {
if (!Internals.usingClientEntryPoint && !__UMD__) {
console.error(
'You are importing hydrateRoot from "react-dom" which is not supported. ' +
'You should instead import it from "react-dom/client".',
);
}
}
return hydrateRootImpl(container, initialChildren, options);
}

// Overload the definition to the two valid signatures.
// Warning, this opts-out of checking the function body.
declare function flushSync<R>(fn: () => R): R;
// eslint-disable-next-line no-redeclare
declare function flushSync(): void;
// eslint-disable-next-line no-redeclare
function flushSync<R>(fn: (() => R) | void): R | void {
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.',
);
}
}
return flushSyncWithoutWarningIfAlreadyRendering(fn);
}

// Expose findDOMNode on internals
Internals.findDOMNode = findDOMNode;

function unstable_batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
// batchedUpdates was a legacy mode feature that is a no-op outside of
// legacy mode. In 19, we made it an actual no-op, but we're keeping it
// for now since there may be libraries that still include it.
return fn(a);
}

export {
createPortal,
unstable_batchedUpdates,
flushSync,
ReactVersion as version,
// Disabled behind disableLegacyReactDOMAPIs
findDOMNode,
render,
unmountComponentAtNode,
// exposeConcurrentModeAPIs
createRoot,
hydrateRoot,
// Disabled behind disableUnstableRenderSubtreeIntoContainer
renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer,
// enableCreateEventHandleAPI
createEventHandle as unstable_createEventHandle,
// TODO: Remove this once callers migrate to alternatives.
// This should only be used by React internals.
runWithPriority as unstable_runWithPriority,
};

// Keep in sync with ReactTestUtils.js.
// This is an array for better minification.
Internals.Events = [
getInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
enqueueStateRestore,
restoreStateIfNeeded,
unstable_batchedUpdates,
];

const foundDevTools = injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
rendererPackageName: 'react-dom',
});

if (__DEV__) {
if (!foundDevTools && canUseDOM && window.top === window.self) {
// If we're in Chrome or Firefox, provide a download link if not installed.
if (
(navigator.userAgent.indexOf('Chrome') > -1 &&
navigator.userAgent.indexOf('Edge') === -1) ||
navigator.userAgent.indexOf('Firefox') > -1
) {
const protocol = window.location.protocol;
// Don't warn in exotic cases like chrome-extension://.
if (/^(https?|file):$/.test(protocol)) {
// eslint-disable-next-line react-internal/no-production-logging
console.info(
'%cDownload the React DevTools ' +
'for a better development experience: ' +
'https://react.dev/link/react-devtools' +
(protocol === 'file:'
? '\nYou might need to use a local HTTP server (instead of file://): ' +
'https://react.dev/link/react-devtools-faq'
: ''),
'font-weight:bold',
);
}
}
}
}
2 changes: 0 additions & 2 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ import {
allowConcurrentByDefault,
disableCommentsAsDOMContainers,
enableAsyncActions,
disableLegacyMode,
} from 'shared/ReactFeatureFlags';
import {flushSync} from '../shared/ReactDOMFlushSync';

export type RootType = {
render(children: ReactNodeList): void,
Expand Down

0 comments on commit 82e73ff

Please sign in to comment.