Skip to content

Commit 1a2ee7a

Browse files
committed
createRoot(..., {hydrate:true}) -> hydrateRoot(...)
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.
1 parent 9212d99 commit 1a2ee7a

10 files changed

+126
-53
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
createPortal,
2424
createRoot,
2525
createRoot as unstable_createRoot, // TODO Remove once callsites use createRoot
26+
hydrateRoot,
2627
findDOMNode,
2728
flushSync,
2829
hydrate,

packages/react-dom/index.experimental.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export {
1111
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
1212
createPortal,
1313
createRoot,
14+
hydrateRoot,
1415
findDOMNode,
1516
flushSync,
1617
hydrate,

packages/react-dom/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
createPortal,
1515
createRoot,
1616
createRoot as unstable_createRoot,
17+
hydrateRoot,
1718
findDOMNode,
1819
flushSync,
1920
hydrate,

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

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export {
1212
createPortal,
1313
createRoot,
1414
createRoot as unstable_createRoot, // TODO Remove once callsites use createRoot
15+
hydrateRoot,
1516
flushSync,
1617
unstable_batchedUpdates,
1718
unstable_createEventHandle,

packages/react-dom/index.stable.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export {
1111
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
1212
createPortal,
1313
createRoot,
14+
hydrateRoot,
1415
findDOMNode,
1516
flushSync,
1617
hydrate,

packages/react-dom/src/__tests__/ReactDOMRoot-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ describe('ReactDOMRoot', () => {
191191
// We care about this warning:
192192
'You are calling ReactDOM.hydrate() on a container that was previously ' +
193193
'passed to ReactDOM.createRoot(). This is not supported. ' +
194-
'Did you mean to call createRoot(container, {hydrate: true}).render(element)?',
194+
'Did you mean to call hydrateRoot(container, element)?',
195195
// This is more of a symptom but restructuring the code to avoid it isn't worth it:
196196
'Replacing React-rendered children with a new root component.',
197197
],

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
unstable_renderSubtreeIntoContainer,
1818
unmountComponentAtNode,
1919
} from './ReactDOMLegacy';
20-
import {createRoot, isValidContainer} from './ReactDOMRoot';
20+
import {createRoot, hydrateRoot, isValidContainer} from './ReactDOMRoot';
2121
import {createEventHandle} from './ReactDOMEventHandle';
2222

2323
import {
@@ -182,6 +182,7 @@ export {
182182
unmountComponentAtNode,
183183
// exposeConcurrentModeAPIs
184184
createRoot,
185+
hydrateRoot,
185186
flushControlled as unstable_flushControlled,
186187
scheduleHydration as unstable_scheduleHydration,
187188
// Disabled behind disableUnstableRenderSubtreeIntoContainer

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ export type EventTargetChildElement = {
105105
...
106106
};
107107
export type Container =
108-
| (Element & {_reactRootContainer?: RootType, ...})
109-
| (Document & {_reactRootContainer?: RootType, ...});
108+
| (Element & {_reactRootContainer?: FiberRoot, ...})
109+
| (Document & {_reactRootContainer?: FiberRoot, ...});
110110
export type Instance = Element;
111111
export type TextInstance = Text;
112112
export type SuspenseInstance = Comment & {_reactRetry?: () => void, ...};

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

+29-19
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,33 @@
88
*/
99

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

1514
import {
1615
getInstanceFromNode,
1716
isContainerMarkedAsRoot,
17+
markContainerAsRoot,
1818
unmarkContainerAsRoot,
1919
} from './ReactDOMComponentTree';
20-
import {createLegacyRoot, isValidContainer} from './ReactDOMRoot';
20+
import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
21+
import {isValidContainerLegacy} from './ReactDOMRoot';
2122
import {
2223
DOCUMENT_NODE,
2324
ELEMENT_NODE,
2425
COMMENT_NODE,
2526
} from '../shared/HTMLNodeType';
2627

2728
import {
29+
createContainer,
2830
findHostInstanceWithNoPortals,
2931
updateContainer,
3032
unbatchedUpdates,
3133
getPublicRootInstance,
3234
findHostInstance,
3335
findHostInstanceWithWarning,
3436
} from 'react-reconciler/src/ReactFiberReconciler';
37+
import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
3538
import getComponentNameFromType from 'shared/getComponentNameFromType';
3639
import invariant from 'shared/invariant';
3740
import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -45,7 +48,7 @@ if (__DEV__) {
4548
topLevelUpdateWarnings = (container: Container) => {
4649
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
4750
const hostInstance = findHostInstanceWithNoPortals(
48-
container._reactRootContainer._internalRoot.current,
51+
container._reactRootContainer.current,
4952
);
5053
if (hostInstance) {
5154
if (hostInstance.parentNode !== container) {
@@ -103,7 +106,7 @@ function getReactRootElementInContainer(container: any) {
103106
function legacyCreateRootFromDOMContainer(
104107
container: Container,
105108
forceHydrate: boolean,
106-
): RootType {
109+
): FiberRoot {
107110
// First clear any existing content.
108111
if (!forceHydrate) {
109112
let rootSibling;
@@ -112,14 +115,21 @@ function legacyCreateRootFromDOMContainer(
112115
}
113116
}
114117

115-
return createLegacyRoot(
118+
const root = createContainer(
116119
container,
117-
forceHydrate
118-
? {
119-
hydrate: true,
120-
}
121-
: undefined,
120+
LegacyRoot,
121+
forceHydrate,
122+
null, // hydrationCallbacks
123+
false, // isStrictMode
124+
false, // concurrentUpdatesByDefaultOverride,
122125
);
126+
markContainerAsRoot(root.current, container);
127+
128+
const rootContainerElement =
129+
container.nodeType === COMMENT_NODE ? container.parentNode : container;
130+
listenToAllSupportedEvents(rootContainerElement);
131+
132+
return root;
123133
}
124134

125135
function warnOnInvalidCallback(callback: mixed, callerName: string): void {
@@ -155,7 +165,7 @@ function legacyRenderSubtreeIntoContainer(
155165
container,
156166
forceHydrate,
157167
);
158-
fiberRoot = root._internalRoot;
168+
fiberRoot = root;
159169
if (typeof callback === 'function') {
160170
const originalCallback = callback;
161171
callback = function() {
@@ -168,7 +178,7 @@ function legacyRenderSubtreeIntoContainer(
168178
updateContainer(children, fiberRoot, parentComponent, callback);
169179
});
170180
} else {
171-
fiberRoot = root._internalRoot;
181+
fiberRoot = root;
172182
if (typeof callback === 'function') {
173183
const originalCallback = callback;
174184
callback = function() {
@@ -221,15 +231,15 @@ export function hydrate(
221231
) {
222232
if (__DEV__) {
223233
console.error(
224-
'ReactDOM.hydrate is no longer supported in React 18. Use createRoot ' +
234+
'ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot ' +
225235
'instead. Until you switch to the new API, your app will behave as ' +
226236
"if it's running React 17. Learn " +
227237
'more: https://reactjs.org/link/switch-to-createroot',
228238
);
229239
}
230240

231241
invariant(
232-
isValidContainer(container),
242+
isValidContainerLegacy(container),
233243
'Target container is not a DOM element.',
234244
);
235245
if (__DEV__) {
@@ -240,7 +250,7 @@ export function hydrate(
240250
console.error(
241251
'You are calling ReactDOM.hydrate() on a container that was previously ' +
242252
'passed to ReactDOM.createRoot(). This is not supported. ' +
243-
'Did you mean to call createRoot(container, {hydrate: true}).render(element)?',
253+
'Did you mean to call hydrateRoot(container, element)?',
244254
);
245255
}
246256
}
@@ -269,7 +279,7 @@ export function render(
269279
}
270280

271281
invariant(
272-
isValidContainer(container),
282+
isValidContainerLegacy(container),
273283
'Target container is not a DOM element.',
274284
);
275285
if (__DEV__) {
@@ -300,7 +310,7 @@ export function unstable_renderSubtreeIntoContainer(
300310
callback: ?Function,
301311
) {
302312
invariant(
303-
isValidContainer(containerNode),
313+
isValidContainerLegacy(containerNode),
304314
'Target container is not a DOM element.',
305315
);
306316
invariant(
@@ -318,7 +328,7 @@ export function unstable_renderSubtreeIntoContainer(
318328

319329
export function unmountComponentAtNode(container: Container) {
320330
invariant(
321-
isValidContainer(container),
331+
isValidContainerLegacy(container),
322332
'unmountComponentAtNode(...): Target container is not a DOM element.',
323333
);
324334

@@ -365,7 +375,7 @@ export function unmountComponentAtNode(container: Container) {
365375
// Check if the container itself is a React root node.
366376
const isContainerReactRoot =
367377
container.nodeType === ELEMENT_NODE &&
368-
isValidContainer(container.parentNode) &&
378+
isValidContainerLegacy(container.parentNode) &&
369379
!!container.parentNode._reactRootContainer;
370380

371381
if (hasNonRootReactChild) {

0 commit comments

Comments
 (0)