Skip to content

Commit

Permalink
Pass children to hydration root constructor
Browse files Browse the repository at this point in the history
I already made this change for the concurrent root API in facebook#23309. This
does the same thing for the legacy API.

Doesn't change any behavior, but I will use this in the next steps.
  • Loading branch information
acdlite committed Mar 12, 2022
1 parent 581f0c4 commit c8e4789
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 56 deletions.
1 change: 0 additions & 1 deletion packages/react-art/src/ReactART.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class Surface extends React.Component {
this._mountNode = createContainer(
this._surface,
LegacyRoot,
false,
null,
false,
false,
Expand Down
9 changes: 9 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMRoot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,15 @@ describe('ReactDOMRoot', () => {
);
});

it('callback passed to legacy hydrate() API', () => {
container.innerHTML = '<div>Hi</div>';
ReactDOM.hydrate(<div>Hi</div>, container, () => {
Scheduler.unstable_yieldValue('callback');
});
expect(container.textContent).toEqual('Hi');
expect(Scheduler).toHaveYielded(['callback']);
});

it('warns when unmounting with legacy API (no previous content)', () => {
const root = ReactDOMClient.createRoot(container);
root.render(<div>Hi</div>);
Expand Down
119 changes: 79 additions & 40 deletions packages/react-dom/src/client/ReactDOMLegacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

import {
createContainer,
createHydrationContainer,
findHostInstanceWithNoPortals,
updateContainer,
flushSync,
Expand Down Expand Up @@ -109,34 +110,81 @@ function noopOnRecoverableError() {

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

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

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

flushSync();
return root;
} else {
// First clear any existing content.
let rootSibling;
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling);
}
}

const root = createContainer(
container,
LegacyRoot,
forceHydrate,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
'', // identifierPrefix
noopOnRecoverableError, // onRecoverableError
null, // transitionCallbacks
);
markContainerAsRoot(root.current, 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
noopOnRecoverableError, // onRecoverableError
null, // transitionCallbacks
);
container._reactRootContainer = root;
markContainerAsRoot(root.current, container);

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

const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);
// Initial mount should not be batched.
flushSync(() => {
updateContainer(initialChildren, root, parentComponent, callback);
});

return root;
return root;
}
}

function warnOnInvalidCallback(callback: mixed, callerName: string): void {
Expand Down Expand Up @@ -164,39 +212,30 @@ function legacyRenderSubtreeIntoContainer(
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}

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

export function findDOMNode(
Expand Down
2 changes: 1 addition & 1 deletion packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ export function createRoot(
const root = createContainer(
container,
ConcurrentRoot,
false,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
Expand Down Expand Up @@ -302,6 +301,7 @@ export function hydrateRoot(

const root = createHydrationContainer(
initialChildren,
null,
container,
ConcurrentRoot,
hydrationCallbacks,
Expand Down
1 change: 0 additions & 1 deletion packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ function render(
root = createContainer(
containerTag,
concurrentRoot ? ConcurrentRoot : LegacyRoot,
false,
null,
false,
null,
Expand Down
1 change: 0 additions & 1 deletion packages/react-native-renderer/src/ReactNativeRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ function render(
root = createContainer(
containerTag,
LegacyRoot,
false,
null,
false,
null,
Expand Down
3 changes: 0 additions & 3 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
root = NoopRenderer.createContainer(
container,
tag,
false,
null,
null,
false,
Expand All @@ -996,7 +995,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
const fiberRoot = NoopRenderer.createContainer(
container,
ConcurrentRoot,
false,
null,
null,
false,
Expand Down Expand Up @@ -1029,7 +1027,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
const fiberRoot = NoopRenderer.createContainer(
container,
LegacyRoot,
false,
null,
null,
false,
Expand Down
8 changes: 5 additions & 3 deletions packages/react-reconciler/src/ReactFiberReconciler.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,14 @@ function findHostInstanceWithWarning(
export function createContainer(
containerInfo: Container,
tag: RootTag,
// TODO: We can remove hydration-specific stuff from createContainer once
// we delete legacy mode. The new root API uses createHydrationContainer.
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: (error: mixed) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
return createFiberRoot(
containerInfo,
tag,
Expand All @@ -270,6 +268,8 @@ export function createContainer(

export function createHydrationContainer(
initialChildren: ReactNodeList,
// TODO: Remove `callback` when we delete legacy mode.
callback: ?Function,
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
Expand Down Expand Up @@ -305,6 +305,8 @@ export function createHydrationContainer(
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element: initialChildren};
update.callback =
callback !== undefined && callback !== null ? callback : null;
enqueueUpdate(current, update, lane);
scheduleInitialHydrationOnRoot(root, lane, eventTime);

Expand Down
8 changes: 5 additions & 3 deletions packages/react-reconciler/src/ReactFiberReconciler.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,14 @@ function findHostInstanceWithWarning(
export function createContainer(
containerInfo: Container,
tag: RootTag,
// TODO: We can remove hydration-specific stuff from createContainer once
// we delete legacy mode. The new root API uses createHydrationContainer.
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: (error: mixed) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
return createFiberRoot(
containerInfo,
tag,
Expand All @@ -270,6 +268,8 @@ export function createContainer(

export function createHydrationContainer(
initialChildren: ReactNodeList,
// TODO: Remove `callback` when we delete legacy mode.
callback: ?Function,
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
Expand Down Expand Up @@ -305,6 +305,8 @@ export function createHydrationContainer(
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element: initialChildren};
update.callback =
callback !== undefined && callback !== null ? callback : null;
enqueueUpdate(current, update, lane);
scheduleInitialHydrationOnRoot(root, lane, eventTime);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ describe('ReactFiberHostContext', () => {
const container = Renderer.createContainer(
/* root: */ null,
ConcurrentRoot,
false,
null,
false,
'',
Expand Down Expand Up @@ -136,7 +135,6 @@ describe('ReactFiberHostContext', () => {
const container = Renderer.createContainer(
rootContext,
ConcurrentRoot,
false,
null,
false,
'',
Expand Down
1 change: 0 additions & 1 deletion packages/react-test-renderer/src/ReactTestRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,6 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
let root: FiberRoot | null = createContainer(
container,
isConcurrent ? ConcurrentRoot : LegacyRoot,
false,
null,
isStrictMode,
concurrentUpdatesByDefault,
Expand Down

0 comments on commit c8e4789

Please sign in to comment.