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);