From c8e4789e21f6cb031b92b3bd8a905244bfd808b2 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 4 Mar 2022 16:50:29 -0500 Subject: [PATCH] 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. --- packages/react-art/src/ReactART.js | 1 - .../src/__tests__/ReactDOMRoot-test.js | 9 ++ .../react-dom/src/client/ReactDOMLegacy.js | 119 ++++++++++++------ packages/react-dom/src/client/ReactDOMRoot.js | 2 +- .../react-native-renderer/src/ReactFabric.js | 1 - .../src/ReactNativeRenderer.js | 1 - .../src/createReactNoop.js | 3 - .../src/ReactFiberReconciler.new.js | 8 +- .../src/ReactFiberReconciler.old.js | 8 +- .../ReactFiberHostContext-test.internal.js | 2 - .../src/ReactTestRenderer.js | 1 - 11 files changed, 99 insertions(+), 56 deletions(-) diff --git a/packages/react-art/src/ReactART.js b/packages/react-art/src/ReactART.js index 9d1b6a16c2038..44cdf4f240a21 100644 --- a/packages/react-art/src/ReactART.js +++ b/packages/react-art/src/ReactART.js @@ -69,7 +69,6 @@ class Surface extends React.Component { this._mountNode = createContainer( this._surface, LegacyRoot, - false, null, false, false, diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js index df693b8784992..9d6a38188376d 100644 --- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js @@ -253,6 +253,15 @@ describe('ReactDOMRoot', () => { ); }); + it('callback passed to legacy hydrate() API', () => { + container.innerHTML = '
Hi
'; + ReactDOM.hydrate(
Hi
, 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(
Hi
); diff --git a/packages/react-dom/src/client/ReactDOMLegacy.js b/packages/react-dom/src/client/ReactDOMLegacy.js index 3b751405a3034..af0e35e128bd4 100644 --- a/packages/react-dom/src/client/ReactDOMLegacy.js +++ b/packages/react-dom/src/client/ReactDOMLegacy.js @@ -27,6 +27,7 @@ import { import { createContainer, + createHydrationContainer, findHostInstanceWithNoPortals, updateContainer, flushSync, @@ -109,34 +110,81 @@ function noopOnRecoverableError() { function legacyCreateRootFromDOMContainer( container: Container, - forceHydrate: boolean, + initialChildren: ReactNodeList, + parentComponent: ?React$Component, + 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 { @@ -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( diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index d71de3bb0c26d..6e7192ae10453 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -225,7 +225,6 @@ export function createRoot( const root = createContainer( container, ConcurrentRoot, - false, null, isStrictMode, concurrentUpdatesByDefaultOverride, @@ -302,6 +301,7 @@ export function hydrateRoot( const root = createHydrationContainer( initialChildren, + null, container, ConcurrentRoot, hydrationCallbacks, diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 127b20fd5dacf..e08a98653fb6c 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -215,7 +215,6 @@ function render( root = createContainer( containerTag, concurrentRoot ? ConcurrentRoot : LegacyRoot, - false, null, false, null, diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 1dca2cd7a1d93..e751195dda00a 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -211,7 +211,6 @@ function render( root = createContainer( containerTag, LegacyRoot, - false, null, false, null, diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index e0ba72076a1af..8e4050dcfa336 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -974,7 +974,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { root = NoopRenderer.createContainer( container, tag, - false, null, null, false, @@ -996,7 +995,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { const fiberRoot = NoopRenderer.createContainer( container, ConcurrentRoot, - false, null, null, false, @@ -1029,7 +1027,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { const fiberRoot = NoopRenderer.createContainer( container, LegacyRoot, - false, null, null, false, diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 8607b227e9b40..67eb7f7b232e5 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -245,9 +245,6 @@ 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, @@ -255,6 +252,7 @@ export function createContainer( onRecoverableError: (error: mixed) => void, transitionCallbacks: null | TransitionTracingCallbacks, ): OpaqueRoot { + const hydrate = false; return createFiberRoot( containerInfo, tag, @@ -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, @@ -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); diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 4970b685b1c1a..378035e95e661 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -245,9 +245,6 @@ 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, @@ -255,6 +252,7 @@ export function createContainer( onRecoverableError: (error: mixed) => void, transitionCallbacks: null | TransitionTracingCallbacks, ): OpaqueRoot { + const hydrate = false; return createFiberRoot( containerInfo, tag, @@ -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, @@ -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); diff --git a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js index d0c3d5b236ea4..82e23de9965da 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js @@ -72,7 +72,6 @@ describe('ReactFiberHostContext', () => { const container = Renderer.createContainer( /* root: */ null, ConcurrentRoot, - false, null, false, '', @@ -136,7 +135,6 @@ describe('ReactFiberHostContext', () => { const container = Renderer.createContainer( rootContext, ConcurrentRoot, - false, null, false, '', diff --git a/packages/react-test-renderer/src/ReactTestRenderer.js b/packages/react-test-renderer/src/ReactTestRenderer.js index e850086439a67..76911d701de79 100644 --- a/packages/react-test-renderer/src/ReactTestRenderer.js +++ b/packages/react-test-renderer/src/ReactTestRenderer.js @@ -473,7 +473,6 @@ function create(element: React$Element, options: TestRendererOptions) { let root: FiberRoot | null = createContainer( container, isConcurrent ? ConcurrentRoot : LegacyRoot, - false, null, isStrictMode, concurrentUpdatesByDefault,