Skip to content

Commit

Permalink
createRoot(..., {hydrate:true}) -> hydrateRoot(...)
Browse files Browse the repository at this point in the history
This adds a new top level API for hydrating a root. It takes the initial
children as part of its constructor. These are unlike other render calls
in that they have to represent what the server sent and they can't be
batched with other updates.

I also changed the options to move the hydrationOptions to the top level
since now these options are all hydration options.

I kept the createRoot one just temporarily to make it easier to codemod
internally but I'm doing a follow up to delete.

As part of this I un-dried a couple of paths. ReactDOMLegacy was intended
to be built on top of the new API but it didn't actually use those root
APIs because there are special paths. It also doesn't actually use most of
the commmon paths since all the options are ignored. It also made it hard
to add only warnings for legacy only or new only code paths.

I also forked the create/hydrate paths because they're subtly different
since now the options are different. The containers are also different
because I now error for comment nodes during hydration which just doesn't
work at all but eventually we'll error for all createRoot calls.

After some iteration it might make sense to break out some common paths but
for now it's easier to iterate on the duplicates.
  • Loading branch information
sebmarkbage committed Jun 15, 2021
1 parent 9212d99 commit f1e62ca
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 52 deletions.
1 change: 1 addition & 0 deletions packages/react-dom/index.classic.fb.js
Expand Up @@ -23,6 +23,7 @@ export {
createPortal,
createRoot,
createRoot as unstable_createRoot, // TODO Remove once callsites use createRoot
hydrateRoot,
findDOMNode,
flushSync,
hydrate,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/index.experimental.js
Expand Up @@ -11,6 +11,7 @@ export {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
createPortal,
createRoot,
hydrateRoot,
findDOMNode,
flushSync,
hydrate,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/index.js
Expand Up @@ -14,6 +14,7 @@ export {
createPortal,
createRoot,
createRoot as unstable_createRoot,
hydrateRoot,
findDOMNode,
flushSync,
hydrate,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/index.modern.fb.js
Expand Up @@ -12,6 +12,7 @@ export {
createPortal,
createRoot,
createRoot as unstable_createRoot, // TODO Remove once callsites use createRoot
hydrateRoot,
flushSync,
unstable_batchedUpdates,
unstable_createEventHandle,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/index.stable.js
Expand Up @@ -11,6 +11,7 @@ export {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
createPortal,
createRoot,
hydrateRoot,
findDOMNode,
flushSync,
hydrate,
Expand Down
3 changes: 2 additions & 1 deletion packages/react-dom/src/client/ReactDOM.js
Expand Up @@ -17,7 +17,7 @@ import {
unstable_renderSubtreeIntoContainer,
unmountComponentAtNode,
} from './ReactDOMLegacy';
import {createRoot, isValidContainer} from './ReactDOMRoot';
import {createRoot, hydrateRoot, isValidContainer} from './ReactDOMRoot';
import {createEventHandle} from './ReactDOMEventHandle';

import {
Expand Down Expand Up @@ -182,6 +182,7 @@ export {
unmountComponentAtNode,
// exposeConcurrentModeAPIs
createRoot,
hydrateRoot,
flushControlled as unstable_flushControlled,
scheduleHydration as unstable_scheduleHydration,
// Disabled behind disableUnstableRenderSubtreeIntoContainer
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Expand Up @@ -105,8 +105,8 @@ export type EventTargetChildElement = {
...
};
export type Container =
| (Element & {_reactRootContainer?: RootType, ...})
| (Document & {_reactRootContainer?: RootType, ...});
| (Element & {_reactRootContainer?: FiberRoot, ...})
| (Document & {_reactRootContainer?: FiberRoot, ...});
export type Instance = Element;
export type TextInstance = Text;
export type SuspenseInstance = Comment & {_reactRetry?: () => void, ...};
Expand Down
48 changes: 29 additions & 19 deletions packages/react-dom/src/client/ReactDOMLegacy.js
Expand Up @@ -8,30 +8,33 @@
*/

import type {Container} from './ReactDOMHostConfig';
import type {RootType} from './ReactDOMRoot';
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {ReactNodeList} from 'shared/ReactTypes';

import {
getInstanceFromNode,
isContainerMarkedAsRoot,
markContainerAsRoot,
unmarkContainerAsRoot,
} from './ReactDOMComponentTree';
import {createLegacyRoot, isValidContainer} from './ReactDOMRoot';
import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
import {isValidContainerLegacy} from './ReactDOMRoot';
import {
DOCUMENT_NODE,
ELEMENT_NODE,
COMMENT_NODE,
} from '../shared/HTMLNodeType';

import {
createContainer,
findHostInstanceWithNoPortals,
updateContainer,
unbatchedUpdates,
getPublicRootInstance,
findHostInstance,
findHostInstanceWithWarning,
} from 'react-reconciler/src/ReactFiberReconciler';
import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
import getComponentNameFromType from 'shared/getComponentNameFromType';
import invariant from 'shared/invariant';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand All @@ -45,7 +48,7 @@ if (__DEV__) {
topLevelUpdateWarnings = (container: Container) => {
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
const hostInstance = findHostInstanceWithNoPortals(
container._reactRootContainer._internalRoot.current,
container._reactRootContainer.current,
);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
Expand Down Expand Up @@ -103,7 +106,7 @@ function getReactRootElementInContainer(container: any) {
function legacyCreateRootFromDOMContainer(
container: Container,
forceHydrate: boolean,
): RootType {
): FiberRoot {
// First clear any existing content.
if (!forceHydrate) {
let rootSibling;
Expand All @@ -112,14 +115,21 @@ function legacyCreateRootFromDOMContainer(
}
}

return createLegacyRoot(
const root = createContainer(
container,
forceHydrate
? {
hydrate: true,
}
: undefined,
LegacyRoot,
forceHydrate,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
);
markContainerAsRoot(root.current, container);

const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);

return root;
}

function warnOnInvalidCallback(callback: mixed, callerName: string): void {
Expand Down Expand Up @@ -155,7 +165,7 @@ function legacyRenderSubtreeIntoContainer(
container,
forceHydrate,
);
fiberRoot = root._internalRoot;
fiberRoot = root;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
Expand All @@ -168,7 +178,7 @@ function legacyRenderSubtreeIntoContainer(
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root._internalRoot;
fiberRoot = root;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
Expand Down Expand Up @@ -221,15 +231,15 @@ export function hydrate(
) {
if (__DEV__) {
console.error(
'ReactDOM.hydrate is no longer supported in React 18. Use createRoot ' +
'ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://reactjs.org/link/switch-to-createroot',
);
}

invariant(
isValidContainer(container),
isValidContainerLegacy(container),
'Target container is not a DOM element.',
);
if (__DEV__) {
Expand All @@ -240,7 +250,7 @@ export function hydrate(
console.error(
'You are calling ReactDOM.hydrate() on a container that was previously ' +
'passed to ReactDOM.createRoot(). This is not supported. ' +
'Did you mean to call createRoot(container, {hydrate: true}).render(element)?',
'Did you mean to call hydrateRoot(container).render(element)?',
);
}
}
Expand Down Expand Up @@ -269,7 +279,7 @@ export function render(
}

invariant(
isValidContainer(container),
isValidContainerLegacy(container),
'Target container is not a DOM element.',
);
if (__DEV__) {
Expand Down Expand Up @@ -300,7 +310,7 @@ export function unstable_renderSubtreeIntoContainer(
callback: ?Function,
) {
invariant(
isValidContainer(containerNode),
isValidContainerLegacy(containerNode),
'Target container is not a DOM element.',
);
invariant(
Expand All @@ -318,7 +328,7 @@ export function unstable_renderSubtreeIntoContainer(

export function unmountComponentAtNode(container: Container) {
invariant(
isValidContainer(container),
isValidContainerLegacy(container),
'unmountComponentAtNode(...): Target container is not a DOM element.',
);

Expand Down Expand Up @@ -365,7 +375,7 @@ export function unmountComponentAtNode(container: Container) {
// Check if the container itself is a React root node.
const isContainerReactRoot =
container.nodeType === ELEMENT_NODE &&
isValidContainer(container.parentNode) &&
isValidContainerLegacy(container.parentNode) &&
!!container.parentNode._reactRootContainer;

if (hasNonRootReactChild) {
Expand Down

0 comments on commit f1e62ca

Please sign in to comment.