diff --git a/packages/react-cs-renderer/src/ReactNativeCS.js b/packages/react-cs-renderer/src/ReactNativeCS.js index 4b87d77ffafa5..b7ed3e8b366a8 100644 --- a/packages/react-cs-renderer/src/ReactNativeCS.js +++ b/packages/react-cs-renderer/src/ReactNativeCS.js @@ -265,7 +265,11 @@ const ReactCS = CSStatefulComponent({ let container = { pendingChild: null, }; - let root = ReactNativeCSFiberRenderer.createContainer(container, false); + let root = ReactNativeCSFiberRenderer.createContainer( + container, + false, + false, + ); return {root, container}; }, render({ diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js index a598cf388991c..85f13de1befce 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js @@ -69,26 +69,18 @@ describe('ReactDOMFiberAsync', () => { ReactFeatureFlags = require('shared/ReactFeatureFlags'); container = document.createElement('div'); ReactFeatureFlags.enableAsyncSubtreeAPI = true; + ReactFeatureFlags.enableCreateRoot = true; ReactDOM = require('react-dom'); }); - it('AsyncComponent at the root makes the entire tree async', () => { - ReactDOM.render( - -
Hi
-
, - container, - ); + it('createRoot makes the entire tree async', () => { + const root = ReactDOM.createRoot(container); + root.render(
Hi
); expect(container.textContent).toEqual(''); jest.runAllTimers(); expect(container.textContent).toEqual('Hi'); - ReactDOM.render( - -
Bye
-
, - container, - ); + root.render(
Bye
); expect(container.textContent).toEqual('Hi'); jest.runAllTimers(); expect(container.textContent).toEqual('Bye'); @@ -104,12 +96,8 @@ describe('ReactDOMFiberAsync', () => { } } - ReactDOM.render( - - - , - container, - ); + const root = ReactDOM.createRoot(container); + root.render(); expect(container.textContent).toEqual(''); jest.runAllTimers(); expect(container.textContent).toEqual('0'); diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js index fd3fd0a07bb79..a16ad96d44e78 100644 --- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js @@ -76,14 +76,17 @@ describe('ReactDOMRoot', () => { it('renders children', () => { const root = ReactDOM.createRoot(container); root.render(
Hi
); + flush(); expect(container.textContent).toEqual('Hi'); }); it('unmounts children', () => { const root = ReactDOM.createRoot(container); root.render(
Hi
); + flush(); expect(container.textContent).toEqual('Hi'); root.unmount(); + flush(); expect(container.textContent).toEqual(''); }); @@ -138,6 +141,7 @@ describe('ReactDOMRoot', () => { , ); + flush(); if (__DEV__) { expect(console.error.calls.count()).toBe(0); } @@ -151,6 +155,7 @@ describe('ReactDOMRoot', () => { , ); + flush(); if (__DEV__) { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toMatch('Extra attributes'); @@ -167,6 +172,7 @@ describe('ReactDOMRoot', () => { d , ); + flush(); expect(container.textContent).toEqual('abcd'); root.render(
@@ -174,6 +180,7 @@ describe('ReactDOMRoot', () => { c
, ); + flush(); expect(container.textContent).toEqual('abdc'); }); diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index f898bb673a418..eb093e71d273a 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -376,8 +376,8 @@ type Root = { _internalRoot: FiberRoot, }; -function ReactRoot(container: Container, hydrate: boolean) { - const root = DOMRenderer.createContainer(container, hydrate); +function ReactRoot(container: Container, isAsync: boolean, hydrate: boolean) { + const root = DOMRenderer.createContainer(container, isAsync, hydrate); this._internalRoot = root; } ReactRoot.prototype.render = function( @@ -1031,8 +1031,9 @@ function legacyCreateRootFromDOMContainer( ); } } - const root: Root = new ReactRoot(container, shouldHydrate); - return root; + // Legacy roots are not async by default. + const isAsync = false; + return new ReactRoot(container, isAsync, shouldHydrate); } function legacyRenderSubtreeIntoContainer( @@ -1300,7 +1301,7 @@ if (enableCreateRoot) { options?: RootOptions, ): ReactRoot { const hydrate = options != null && options.hydrate === true; - return new ReactRoot(container, hydrate); + return new ReactRoot(container, true, hydrate); }; } diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 792bca955f329..483a049c2c15c 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -48,7 +48,11 @@ const ReactNativeRenderer: ReactNativeType = { if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. - root = ReactNativeFiberRenderer.createContainer(containerTag, false); + root = ReactNativeFiberRenderer.createContainer( + containerTag, + false, + false, + ); roots.set(containerTag, root); } ReactNativeFiberRenderer.updateContainer(element, root, null, callback); diff --git a/packages/react-noop-renderer/src/ReactNoop.js b/packages/react-noop-renderer/src/ReactNoop.js index ab778ddbce658..f84e01d42c3fd 100644 --- a/packages/react-noop-renderer/src/ReactNoop.js +++ b/packages/react-noop-renderer/src/ReactNoop.js @@ -341,7 +341,7 @@ var ReactNoop = { if (!root) { const container = {rootID: rootID, children: []}; rootContainers.set(rootID, container); - root = NoopRenderer.createContainer(container, false); + root = NoopRenderer.createContainer(container, true, false); roots.set(rootID, root); } NoopRenderer.updateContainer(element, root, null, callback); @@ -361,7 +361,7 @@ var ReactNoop = { if (!root) { const container = {rootID: rootID, children: []}; rootContainers.set(rootID, container); - root = PersistentNoopRenderer.createContainer(container, false); + root = PersistentNoopRenderer.createContainer(container, true, false); persistentRoots.set(rootID, root); } PersistentNoopRenderer.updateContainer(element, root, null, callback); diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 257dcd65a2442..7d19081d36b64 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -35,7 +35,7 @@ import { import getComponentName from 'shared/getComponentName'; import {NoWork} from './ReactFiberExpirationTime'; -import {NoContext} from './ReactTypeOfInternalContext'; +import {NoContext, AsyncUpdates} from './ReactTypeOfInternalContext'; if (__DEV__) { var hasBadMapPolyfill = false; @@ -288,9 +288,9 @@ export function createWorkInProgress( return workInProgress; } -export function createHostRootFiber(): Fiber { - const fiber = createFiber(HostRoot, null, NoContext); - return fiber; +export function createHostRootFiber(isAsync): Fiber { + const internalContextTag = isAsync ? AsyncUpdates : NoContext; + return createFiber(HostRoot, null, null, internalContextTag); } export function createFiberFromElement( diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js index dac06f0dd1e14..b5b319047f423 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.js @@ -12,7 +12,6 @@ import type {FiberRoot} from './ReactFiberRoot'; import type {ReactNodeList} from 'shared/ReactTypes'; import type {ExpirationTime} from './ReactFiberExpirationTime'; -import {enableAsyncSubtreeAPI} from 'shared/ReactFeatureFlags'; import { findCurrentHostFiber, findCurrentHostFiberWithNoPortals, @@ -233,7 +232,11 @@ type DevToolsConfig = {| |}; export type Reconciler = { - createContainer(containerInfo: C, hydrate: boolean): OpaqueRoot, + createContainer( + containerInfo: C, + isAsync: boolean, + hydrate: boolean, + ): OpaqueRoot, updateContainer( element: ReactNodeList, container: OpaqueRoot, @@ -288,7 +291,6 @@ export default function( const {getPublicInstance} = config; const { - computeAsyncExpiration, computeUniqueAsyncExpiration, computeExpirationForFiber, scheduleWork, @@ -300,25 +302,6 @@ export default function( deferredUpdates, } = ReactFiberScheduler(config); - function computeRootExpirationTime(current, element) { - let expirationTime; - // Check if the top-level element is an async wrapper component. If so, - // treat updates to the root as async. This is a bit weird but lets us - // avoid a separate `renderAsync` API. - if ( - enableAsyncSubtreeAPI && - element != null && - (element: any).type != null && - (element: any).type.prototype != null && - (element: any).type.prototype.unstable_isAsyncReactComponent === true - ) { - expirationTime = computeAsyncExpiration(); - } else { - expirationTime = computeExpirationForFiber(current); - } - return expirationTime; - } - function scheduleRootUpdate( current: Fiber, element: ReactNodeList, @@ -408,8 +391,12 @@ export default function( } return { - createContainer(containerInfo: C, hydrate: boolean): OpaqueRoot { - return createFiberRoot(containerInfo, hydrate); + createContainer( + containerInfo: C, + isAsync: boolean, + hydrate: boolean, + ): OpaqueRoot { + return createFiberRoot(containerInfo, isAsync, hydrate); }, updateContainer( @@ -419,7 +406,7 @@ export default function( callback: ?Function, ): ExpirationTime { const current = container.current; - const expirationTime = computeRootExpirationTime(current, element); + const expirationTime = computeExpirationForFiber(current); return updateContainerAtExpirationTime( element, container, diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js index b6e21ed6872e8..1dd87cc14b3e4 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.js +++ b/packages/react-reconciler/src/ReactFiberRoot.js @@ -52,11 +52,12 @@ export type FiberRoot = { export function createFiberRoot( containerInfo: any, + isAsync: boolean, hydrate: boolean, ): FiberRoot { // Cyclic construction. This cheats the type system right now because // stateNode is any. - const uninitializedFiber = createHostRootFiber(); + const uninitializedFiber = createHostRootFiber(isAsync); const root = { current: uninitializedFiber, containerInfo: containerInfo, diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js index c69f4bbcb3a30..1a738e9624857 100644 --- a/packages/react-reconciler/src/ReactFiberScheduler.js +++ b/packages/react-reconciler/src/ReactFiberScheduler.js @@ -1738,7 +1738,6 @@ export default function( } return { - computeAsyncExpiration, computeExpirationForFiber, scheduleWork, requestWork, diff --git a/packages/react-rt-renderer/src/ReactNativeRT.js b/packages/react-rt-renderer/src/ReactNativeRT.js index 5b72f15fdee6a..9a21c4dc0ae3a 100644 --- a/packages/react-rt-renderer/src/ReactNativeRT.js +++ b/packages/react-rt-renderer/src/ReactNativeRT.js @@ -40,7 +40,11 @@ const ReactNativeRTFiber: ReactNativeRTType = { if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. - root = ReactNativeRTFiberRenderer.createContainer(containerTag, false); + root = ReactNativeRTFiberRenderer.createContainer( + containerTag, + false, + false, + ); roots.set(containerTag, root); } ReactNativeRTFiberRenderer.updateContainer(element, root, null, callback); diff --git a/packages/react-test-renderer/src/ReactTestRenderer.js b/packages/react-test-renderer/src/ReactTestRenderer.js index 24cd1f4bc5129..2cb3d9eecb67c 100644 --- a/packages/react-test-renderer/src/ReactTestRenderer.js +++ b/packages/react-test-renderer/src/ReactTestRenderer.js @@ -572,7 +572,11 @@ const ReactTestRendererFiber = { createNodeMock, tag: 'CONTAINER', }; - let root: FiberRoot | null = TestRenderer.createContainer(container, false); + let root: FiberRoot | null = TestRenderer.createContainer( + container, + false, + false, + ); invariant(root != null, 'something went wrong'); TestRenderer.updateContainer(element, root, null, null);