Skip to content

Commit

Permalink
[fb][react-dom] remove render/unmount from ReactDOMRootFB
Browse files Browse the repository at this point in the history
  • Loading branch information
kassens committed Jan 3, 2025
1 parent bf883be commit 9d80d4c
Show file tree
Hide file tree
Showing 5 changed files with 2 additions and 399 deletions.
13 changes: 0 additions & 13 deletions packages/react-dom-bindings/src/client/ReactDOMContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,3 @@ export function isValidContainer(node: any): boolean {
(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 '))
);
}
2 changes: 0 additions & 2 deletions packages/react-dom/src/ReactDOMFB.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ export {
export {
createRoot,
hydrateRoot,
render,
unstable_batchedUpdates,
findDOMNode,
unmountComponentAtNode,
} from './client/ReactDOMRootFB';
337 changes: 1 addition & 336 deletions packages/react-dom/src/client/ReactDOMRootFB.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,20 @@ import type {
HydrateRootOptions,
} from './ReactDOMRoot';

import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';

import type {
Container,
PublicInstance,
} from 'react-dom-bindings/src/client/ReactFiberConfigDOM';

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

import {disableLegacyMode} from 'shared/ReactFeatureFlags';
import {clearContainer} from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
import {
getInstanceFromNode,
isContainerMarkedAsRoot,
markContainerAsRoot,
unmarkContainerAsRoot,
} from 'react-dom-bindings/src/client/ReactDOMComponentTree';
import {listenToAllSupportedEvents} from 'react-dom-bindings/src/events/DOMPluginEventSystem';
import {isValidContainerLegacy} from 'react-dom-bindings/src/client/ReactDOMContainer';
import {
DOCUMENT_NODE,
ELEMENT_NODE,
COMMENT_NODE,
} from 'react-dom-bindings/src/client/HTMLNodeType';
import {ELEMENT_NODE} from 'react-dom-bindings/src/client/HTMLNodeType';

import {
batchedUpdates,
createContainer,
createHydrationContainer,
findHostInstanceWithNoPortals,
updateContainer,
updateContainerSync,
flushSyncWork,
getPublicRootInstance,
findHostInstance,
findHostInstanceWithWarning,
defaultOnUncaughtError,
defaultOnCaughtError,
} from 'react-reconciler/src/ReactFiberReconciler';
import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
import getComponentNameFromType from 'shared/getComponentNameFromType';

import {
Expand Down Expand Up @@ -156,190 +127,6 @@ export function hydrateRoot(
);
}

let topLevelUpdateWarnings;

if (__DEV__) {
topLevelUpdateWarnings = (container: Container) => {
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
const hostInstance = findHostInstanceWithNoPortals(
container._reactRootContainer.current,
);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
console.error(
'It looks like the React-rendered content of this ' +
'container was removed without using React. This is not ' +
'supported and will cause errors. Instead, call ' +
'ReactDOM.unmountComponentAtNode to empty a container.',
);
}
}
}

const isRootRenderedBySomeReact = !!container._reactRootContainer;
const rootEl = getReactRootElementInContainer(container);
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));

if (hasNonRootReactChild && !isRootRenderedBySomeReact) {
console.error(
'Replacing React-rendered children with a new root ' +
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.',
);
}
};
}

function getReactRootElementInContainer(container: any) {
if (!container) {
return null;
}

if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}

function noopOnRecoverableError() {
// This isn't reachable because onRecoverableError isn't called in the
// legacy API.
}

function legacyCreateRootFromDOMContainer(
container: Container,
initialChildren: ReactNodeList,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
isHydrationContainer: boolean,
): FiberRoot {
if (isHydrationContainer) {
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}

const root: FiberRoot = createHydrationContainer(
initialChildren,
callback,
container,
LegacyRoot,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
'', // identifierPrefix
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
// TODO(luna) Support hydration later
null,
null,
);
container._reactRootContainer = root;
markContainerAsRoot(root.current, container);

const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
// $FlowFixMe[incompatible-call]
listenToAllSupportedEvents(rootContainerElement);

flushSyncWork();
return root;
} else {
// First clear any existing content.
clearContainer(container);

if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}

const root = createContainer(
container,
LegacyRoot,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
'', // identifierPrefix
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
null, // transitionCallbacks
);
container._reactRootContainer = root;
markContainerAsRoot(root.current, container);

const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
// $FlowFixMe[incompatible-call]
listenToAllSupportedEvents(rootContainerElement);

// Initial mount should not be batched.
updateContainerSync(initialChildren, root, parentComponent, callback);
flushSyncWork();

return root;
}
}

function warnOnInvalidCallback(callback: mixed): void {
if (__DEV__) {
if (callback !== null && typeof callback !== 'function') {
console.error(
'Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
}

function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
): React$Component<any, any> | PublicInstance | null {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback);
}

const maybeRoot = container._reactRootContainer;
let root: FiberRoot;
if (!maybeRoot) {
// Initial mount
root = legacyCreateRootFromDOMContainer(
container,
children,
parentComponent,
callback,
forceHydrate,
);
} else {
root = maybeRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}
// Update
updateContainer(children, root, parentComponent, callback);
}
return getPublicRootInstance(root);
}

export function findDOMNode(
componentOrElement: Element | ?React$Component<any, any>,
): null | Element | Text {
Expand Down Expand Up @@ -372,126 +159,4 @@ export function findDOMNode(
return findHostInstance(componentOrElement);
}

export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
): React$Component<any, any> | PublicInstance | null {
if (disableLegacyMode) {
if (__DEV__) {
console.error(
'ReactDOM.render was removed in React 19. Use createRoot instead.',
);
}
throw new Error('ReactDOM: Unsupported Legacy Mode API.');
}
if (__DEV__) {
console.error(
'ReactDOM.render has not been supported since React 18. Use createRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://react.dev/link/switch-to-createroot',
);
}

if (!isValidContainerLegacy(container)) {
throw new Error('Target container is not a DOM element.');
}

if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
if (isModernRoot) {
console.error(
'You are calling ReactDOM.render() on a container that was previously ' +
'passed to ReactDOMClient.createRoot(). This is not supported. ' +
'Did you mean to call root.render(element)?',
);
}
}
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}

export function unmountComponentAtNode(container: Container): boolean {
if (disableLegacyMode) {
if (__DEV__) {
console.error(
'unmountComponentAtNode was removed in React 19. Use root.unmount() instead.',
);
}
throw new Error('ReactDOM: Unsupported Legacy Mode API.');
}
if (!isValidContainerLegacy(container)) {
throw new Error('Target container is not a DOM element.');
}

if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
if (isModernRoot) {
console.error(
'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
'passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()?',
);
}
}

if (container._reactRootContainer) {
const root = container._reactRootContainer;

if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
if (renderedByDifferentReact) {
console.error(
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
}
}

updateContainerSync(null, root, null, null);
flushSyncWork();
// $FlowFixMe[incompatible-type] This should probably use `delete container._reactRootContainer`
container._reactRootContainer = null;
unmarkContainerAsRoot(container);
return true;
} else {
if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));

// Check if the container itself is a React root node.
const isContainerReactRoot =
container.nodeType === ELEMENT_NODE &&
isValidContainerLegacy(container.parentNode) &&
// $FlowFixMe[prop-missing]
// $FlowFixMe[incompatible-use]
!!container.parentNode._reactRootContainer;

if (hasNonRootReactChild) {
console.error(
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by React and is not a top-level container. %s',
isContainerReactRoot
? 'You may have accidentally passed in a React root node instead ' +
'of its container.'
: 'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.',
);
}
}

return false;
}
}

export {batchedUpdates as unstable_batchedUpdates};
Loading

0 comments on commit 9d80d4c

Please sign in to comment.