diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index 72beeb88e7d28..7ad2fe0e2bb38 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -542,6 +542,8 @@ export function startSuspendingCommit() {} export function suspendInstance(type, props) {} +export function suspendOnActiveViewTransition(container) {} + export function waitForCommitToBeReady() { return null; } diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 1a39cbdbb0256..93b80f13479f2 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1219,8 +1219,14 @@ export function startViewTransition( }, types: null, // TODO: Provide types. }); + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = transition; transition.ready.then(layoutCallback, layoutCallback); - transition.finished.then(passiveCallback); + transition.finished.then(() => { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + passiveCallback(); + }); return true; } catch (x) { // We use the error as feature detection. @@ -3706,6 +3712,27 @@ export function suspendResource( } } +export function suspendOnActiveViewTransition(rootContainer: Container): void { + if (suspendedState === null) { + throw new Error( + 'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.', + ); + } + const state = suspendedState; + const ownerDocument = + rootContainer.nodeType === DOCUMENT_NODE + ? rootContainer + : rootContainer.ownerDocument; + // $FlowFixMe[prop-missing] + const activeViewTransition = ownerDocument.__reactViewTransition; + if (activeViewTransition == null) { + return; + } + state.count++; + const ping = onUnsuspend.bind(state); + activeViewTransition.finished.then(ping, ping); +} + export function waitForCommitToBeReady(): null | ((() => void) => () => void) { if (suspendedState === null) { throw new Error( diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 2b0997d2e8b3f..200f1662eb589 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -551,6 +551,8 @@ export function startSuspendingCommit(): void {} export function suspendInstance(type: Type, props: Props): void {} +export function suspendOnActiveViewTransition(container: Container): void {} + export function waitForCommitToBeReady(): null { return null; } diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 12aa16645ea5b..5687e60637d11 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -637,6 +637,8 @@ export function startSuspendingCommit(): void {} export function suspendInstance(type: Type, props: Props): void {} +export function suspendOnActiveViewTransition(container: Container): void {} + export function waitForCommitToBeReady(): null { return null; } diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 6215d9ea3611f..6536821c1e773 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -643,6 +643,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { ); }, + suspendOnActiveViewTransition(container: Container): void { + // Not implemented + }, + waitForCommitToBeReady, NotPendingTransition: (null: TransitionStatus), diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 0c82fd1ee2bdc..d73e4f7002bd2 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -85,6 +85,7 @@ import { noTimeout, afterActiveInstanceBlur, startSuspendingCommit, + suspendOnActiveViewTransition, waitForCommitToBeReady, preloadInstance, preloadResource, @@ -1393,11 +1394,13 @@ function commitRootWhenReady( // one after the other. const BothVisibilityAndMaySuspendCommit = Visibility | MaySuspendCommit; const subtreeFlags = finishedWork.subtreeFlags; - if ( + const isViewTransitionEligible = + enableViewTransition && includesOnlyViewTransitionEligibleLanes(lanes); // TODO: Use a subtreeFlag to optimize. + const maySuspendCommit = subtreeFlags & ShouldSuspendCommit || (subtreeFlags & BothVisibilityAndMaySuspendCommit) === - BothVisibilityAndMaySuspendCommit - ) { + BothVisibilityAndMaySuspendCommit; + if (isViewTransitionEligible || maySuspendCommit) { // Before committing, ask the renderer whether the host tree is ready. // If it's not, we'll wait until it notifies us. startSuspendingCommit(); @@ -1405,7 +1408,12 @@ function commitRootWhenReady( // the suspensey resources. The renderer is responsible for accumulating // all the load events. This all happens in a single synchronous // transaction, so it track state in its own module scope. - accumulateSuspenseyCommit(finishedWork); + if (maySuspendCommit) { + accumulateSuspenseyCommit(finishedWork); + } + if (isViewTransitionEligible) { + suspendOnActiveViewTransition(root.containerInfo); + } // At the end, ask the renderer if it's ready to commit, or if we should // suspend. If it's not ready, it will return a callback to subscribe to // a ready event. diff --git a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js index fbbe9a3bf71af..7ceba3439f6ce 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js @@ -102,6 +102,7 @@ describe('ReactFiberHostContext', () => { }, startSuspendingCommit() {}, suspendInstance(type, props) {}, + suspendOnActiveViewTransition(container) {}, waitForCommitToBeReady() { return null; }, diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 5ebdf2d2b3332..10e419f2c08ef 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -85,6 +85,8 @@ export const maySuspendCommit = $$$config.maySuspendCommit; export const preloadInstance = $$$config.preloadInstance; export const startSuspendingCommit = $$$config.startSuspendingCommit; export const suspendInstance = $$$config.suspendInstance; +export const suspendOnActiveViewTransition = + $$$config.suspendOnActiveViewTransition; export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady; export const NotPendingTransition = $$$config.NotPendingTransition; export const HostTransitionContext = $$$config.HostTransitionContext; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 29fb75079ecaa..15db19c3c96da 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -425,6 +425,8 @@ export function startSuspendingCommit(): void {} export function suspendInstance(type: Type, props: Props): void {} +export function suspendOnActiveViewTransition(container: Container): void {} + export function waitForCommitToBeReady(): null { return null; }