Pass children to hydration root constructor
I already made this change for the concurrent root API in #23309. This does the same thing for the legacy API. Doesn't change any behavior, but I will use this in the next steps.
Andrew Clark committed
Mar 4, 2022 at 16:50 UTC
c8e4789e21f6cb031b92b3bd8a905244bfd808b2
11 files changed
+99
-56
packages/react-art/src/ReactART.js
-1
@@ -69,7 +69,6 @@ class Surface extends React.Component {
69
this._mountNode = createContainer(
70
this._surface,
71
LegacyRoot,
72
- false,
72
null,
73
false,
74
false,
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+9
@@ -253,6 +253,15 @@ describe('ReactDOMRoot', () => {
253
);
254
});
255
256
+ it('callback passed to legacy hydrate() API', () => {
257
+ container.innerHTML = '<div>Hi</div>';
258
+ ReactDOM.hydrate(<div>Hi</div>, container, () => {
259
+ Scheduler.unstable_yieldValue('callback');
260
+ });
261
+ expect(container.textContent).toEqual('Hi');
262
+ expect(Scheduler).toHaveYielded(['callback']);
263
+ });
264
+
265
it('warns when unmounting with legacy API (no previous content)', () => {
266
const root = ReactDOMClient.createRoot(container);
267
root.render(<div>Hi</div>);
packages/react-dom/src/client/ReactDOMLegacy.js
+79
-40
@@ -27,6 +27,7 @@ import {
27
28
import {
29
createContainer,
30
+ createHydrationContainer,
31
findHostInstanceWithNoPortals,
32
updateContainer,
33
flushSync,
@@ -109,34 +110,81 @@ function noopOnRecoverableError() {
110
111
function legacyCreateRootFromDOMContainer(
112
container: Container,
112
- forceHydrate: boolean,
113
+ initialChildren: ReactNodeList,
114
+ parentComponent: ?React$Component<any, any>,
115
+ callback: ?Function,
116
+ isHydrationContainer: boolean,
117
): FiberRoot {
114
- // First clear any existing content.
115
- if (!forceHydrate) {
118
+ if (isHydrationContainer) {
119
+ if (typeof callback === 'function') {
120
+ const originalCallback = callback;
121
+ callback = function() {
122
+ const instance = getPublicRootInstance(root);
123
+ originalCallback.call(instance);
124
+ };
125
+ }
126
+
127
+ const root = createHydrationContainer(
128
+ initialChildren,
129
+ callback,
130
+ container,
131
+ LegacyRoot,
132
+ null, // hydrationCallbacks
133
+ false, // isStrictMode
134
+ false, // concurrentUpdatesByDefaultOverride,
135
+ '', // identifierPrefix
136
+ noopOnRecoverableError,
137
+ // TODO(luna) Support hydration later
138
+ null,
139
+ );
140
+ container._reactRootContainer = root;
141
+ markContainerAsRoot(root.current, container);
142
+
143
+ const rootContainerElement =
144
+ container.nodeType === COMMENT_NODE ? container.parentNode : container;
145
+ listenToAllSupportedEvents(rootContainerElement);
146
+
147
+ flushSync();
148
+ return root;
149
+ } else {
150
+ // First clear any existing content.
151
let rootSibling;
152
while ((rootSibling = container.lastChild)) {
153
container.removeChild(rootSibling);
154
}
120
- }
155
122
- const root = createContainer(
123
- container,
124
- LegacyRoot,
125
- forceHydrate,
126
- null, // hydrationCallbacks
127
- false, // isStrictMode
128
- false, // concurrentUpdatesByDefaultOverride,
129
- '', // identifierPrefix
130
- noopOnRecoverableError, // onRecoverableError
131
- null, // transitionCallbacks
132
- );
133
- markContainerAsRoot(root.current, container);
156
+ if (typeof callback === 'function') {
157
+ const originalCallback = callback;
158
+ callback = function() {
159
+ const instance = getPublicRootInstance(root);
160
+ originalCallback.call(instance);
161
+ };
162
+ }
163
+
164
+ const root = createContainer(
165
+ container,
166
+ LegacyRoot,
167
+ null, // hydrationCallbacks
168
+ false, // isStrictMode
169
+ false, // concurrentUpdatesByDefaultOverride,
170
+ '', // identifierPrefix
171
+ noopOnRecoverableError, // onRecoverableError
172
+ null, // transitionCallbacks
173
+ );
174
+ container._reactRootContainer = root;
175
+ markContainerAsRoot(root.current, container);
176
+
177
+ const rootContainerElement =
178
+ container.nodeType === COMMENT_NODE ? container.parentNode : container;
179
+ listenToAllSupportedEvents(rootContainerElement);
180
135
- const rootContainerElement =
136
- container.nodeType === COMMENT_NODE ? container.parentNode : container;
137
- listenToAllSupportedEvents(rootContainerElement);
181
+ // Initial mount should not be batched.
182
+ flushSync(() => {
183
+ updateContainer(initialChildren, root, parentComponent, callback);
184
+ });
185
139
- return root;
186
+ return root;
187
+ }
188
}
189
190
function warnOnInvalidCallback(callback: mixed, callerName: string): void {
@@ -164,39 +212,30 @@ function legacyRenderSubtreeIntoContainer(
212
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
213
}
214
167
- let root = container._reactRootContainer;
168
- let fiberRoot: FiberRoot;
169
- if (!root) {
215
+ const maybeRoot = container._reactRootContainer;
216
+ let root: FiberRoot;
217
+ if (!maybeRoot) {
218
// Initial mount
171
- root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
219
+ root = legacyCreateRootFromDOMContainer(
220
container,
221
+ children,
222
+ parentComponent,
223
+ callback,
224
forceHydrate,
225
);
175
- fiberRoot = root;
176
- if (typeof callback === 'function') {
177
- const originalCallback = callback;
178
- callback = function() {
179
- const instance = getPublicRootInstance(fiberRoot);
180
- originalCallback.call(instance);
181
- };
182
- }
183
- // Initial mount should not be batched.
184
- flushSync(() => {
185
- updateContainer(children, fiberRoot, parentComponent, callback);
186
- });
226
} else {
188
- fiberRoot = root;
227
+ root = maybeRoot;
228
if (typeof callback === 'function') {
229
const originalCallback = callback;
230
callback = function() {
192
- const instance = getPublicRootInstance(fiberRoot);
231
+ const instance = getPublicRootInstance(root);
232
originalCallback.call(instance);
233
};
234
}
235
// Update
197
- updateContainer(children, fiberRoot, parentComponent, callback);
236
+ updateContainer(children, root, parentComponent, callback);
237
}
199
- return getPublicRootInstance(fiberRoot);
238
+ return getPublicRootInstance(root);
239
}
240
241
export function findDOMNode(
packages/react-dom/src/client/ReactDOMRoot.js
+1
-1
@@ -225,7 +225,6 @@ export function createRoot(
225
const root = createContainer(
226
container,
227
ConcurrentRoot,
228
- false,
228
null,
229
isStrictMode,
230
concurrentUpdatesByDefaultOverride,
@@ -302,6 +301,7 @@ export function hydrateRoot(
301
302
const root = createHydrationContainer(
303
initialChildren,
304
+ null,
305
container,
306
ConcurrentRoot,
307
hydrationCallbacks,
packages/react-native-renderer/src/ReactFabric.js
-1
@@ -215,7 +215,6 @@ function render(
215
root = createContainer(
216
containerTag,
217
concurrentRoot ? ConcurrentRoot : LegacyRoot,
218
- false,
218
null,
219
false,
220
null,
packages/react-native-renderer/src/ReactNativeRenderer.js
-1
@@ -211,7 +211,6 @@ function render(
211
root = createContainer(
212
containerTag,
213
LegacyRoot,
214
- false,
214
null,
215
false,
216
null,
packages/react-noop-renderer/src/createReactNoop.js
-3
@@ -974,7 +974,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
974
root = NoopRenderer.createContainer(
975
container,
976
tag,
977
- false,
977
null,
978
null,
979
false,
@@ -996,7 +995,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
995
const fiberRoot = NoopRenderer.createContainer(
996
container,
997
ConcurrentRoot,
999
- false,
998
null,
999
null,
1000
false,
@@ -1029,7 +1027,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
1027
const fiberRoot = NoopRenderer.createContainer(
1028
container,
1029
LegacyRoot,
1032
- false,
1030
null,
1031
null,
1032
false,
packages/react-reconciler/src/ReactFiberReconciler.new.js
+5
-3
@@ -245,9 +245,6 @@ function findHostInstanceWithWarning(
245
export function createContainer(
246
containerInfo: Container,
247
tag: RootTag,
248
- // TODO: We can remove hydration-specific stuff from createContainer once
249
- // we delete legacy mode. The new root API uses createHydrationContainer.
250
- hydrate: boolean,
248
hydrationCallbacks: null | SuspenseHydrationCallbacks,
249
isStrictMode: boolean,
250
concurrentUpdatesByDefaultOverride: null | boolean,
@@ -255,6 +252,7 @@ export function createContainer(
252
onRecoverableError: (error: mixed) => void,
253
transitionCallbacks: null | TransitionTracingCallbacks,
254
): OpaqueRoot {
255
+ const hydrate = false;
256
return createFiberRoot(
257
containerInfo,
258
tag,
@@ -270,6 +268,8 @@ export function createContainer(
268
269
export function createHydrationContainer(
270
initialChildren: ReactNodeList,
271
+ // TODO: Remove `callback` when we delete legacy mode.
272
+ callback: ?Function,
273
containerInfo: Container,
274
tag: RootTag,
275
hydrationCallbacks: null | SuspenseHydrationCallbacks,
@@ -305,6 +305,8 @@ export function createHydrationContainer(
305
// Caution: React DevTools currently depends on this property
306
// being called "element".
307
update.payload = {element: initialChildren};
308
+ update.callback =
309
+ callback !== undefined && callback !== null ? callback : null;
310
enqueueUpdate(current, update, lane);
311
scheduleInitialHydrationOnRoot(root, lane, eventTime);
312
packages/react-reconciler/src/ReactFiberReconciler.old.js
+5
-3
@@ -245,9 +245,6 @@ function findHostInstanceWithWarning(
245
export function createContainer(
246
containerInfo: Container,
247
tag: RootTag,
248
- // TODO: We can remove hydration-specific stuff from createContainer once
249
- // we delete legacy mode. The new root API uses createHydrationContainer.
250
- hydrate: boolean,
248
hydrationCallbacks: null | SuspenseHydrationCallbacks,
249
isStrictMode: boolean,
250
concurrentUpdatesByDefaultOverride: null | boolean,
@@ -255,6 +252,7 @@ export function createContainer(
252
onRecoverableError: (error: mixed) => void,
253
transitionCallbacks: null | TransitionTracingCallbacks,
254
): OpaqueRoot {
255
+ const hydrate = false;
256
return createFiberRoot(
257
containerInfo,
258
tag,
@@ -270,6 +268,8 @@ export function createContainer(
268
269
export function createHydrationContainer(
270
initialChildren: ReactNodeList,
271
+ // TODO: Remove `callback` when we delete legacy mode.
272
+ callback: ?Function,
273
containerInfo: Container,
274
tag: RootTag,
275
hydrationCallbacks: null | SuspenseHydrationCallbacks,
@@ -305,6 +305,8 @@ export function createHydrationContainer(
305
// Caution: React DevTools currently depends on this property
306
// being called "element".
307
update.payload = {element: initialChildren};
308
+ update.callback =
309
+ callback !== undefined && callback !== null ? callback : null;
310
enqueueUpdate(current, update, lane);
311
scheduleInitialHydrationOnRoot(root, lane, eventTime);
312
packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js
-2
@@ -72,7 +72,6 @@ describe('ReactFiberHostContext', () => {
72
const container = Renderer.createContainer(
73
/* root: */ null,
74
ConcurrentRoot,
75
- false,
75
null,
76
false,
77
'',
@@ -136,7 +135,6 @@ describe('ReactFiberHostContext', () => {
135
const container = Renderer.createContainer(
136
rootContext,
137
ConcurrentRoot,
139
- false,
138
null,
139
false,
140
'',
packages/react-test-renderer/src/ReactTestRenderer.js
-1
@@ -473,7 +473,6 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
473
let root: FiberRoot | null = createContainer(
474
container,
475
isConcurrent ? ConcurrentRoot : LegacyRoot,
476
- false,
476
null,
477
isStrictMode,
478
concurrentUpdatesByDefault,