diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js
index fbaa9017171..6227e9ebc5f 100644
--- a/fixtures/view-transition/src/components/Page.js
+++ b/fixtures/view-transition/src/components/Page.js
@@ -171,17 +171,20 @@ export default function Page({url, navigate}) {
}}>
{!show ? 'A' + counter : 'B'}
- {show ? (
-
- {a}
- {b}
-
- ) : (
-
- {b}
- {a}
-
- )}
+ {
+ // Using url instead of renderedUrl here lets us only update this on commit.
+ url === '/?b' ? (
+
+ {a}
+ {b}
+
+ ) : (
+
+ {b}
+ {a}
+
+ )
+ }
{show ? (
hello{exclamation}
diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js
index 20abaa673c2..64940c31957 100644
--- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js
+++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js
@@ -711,7 +711,11 @@ function measureViewTransitionHostInstancesRecursive(
}
viewTransitionCancelableChildren.push(
instance,
- oldName,
+ viewTransitionHostInstanceIdx === 0
+ ? oldName
+ : // If we have multiple Host Instances below, we add a suffix to the name to give
+ // each one a unique name.
+ oldName + '_' + viewTransitionHostInstanceIdx,
child.memoizedProps,
);
}
diff --git a/packages/react-reconciler/src/ReactFiberGestureScheduler.js b/packages/react-reconciler/src/ReactFiberGestureScheduler.js
index ed61c59b05a..144d5f3aa5f 100644
--- a/packages/react-reconciler/src/ReactFiberGestureScheduler.js
+++ b/packages/react-reconciler/src/ReactFiberGestureScheduler.js
@@ -11,14 +11,21 @@ import type {FiberRoot} from './ReactInternalTypes';
import type {GestureOptions} from 'shared/ReactTypes';
import type {GestureTimeline, RunningViewTransition} from './ReactFiberConfig';
import type {TransitionTypes} from 'react/src/ReactTransitionType';
+import type {Lane} from './ReactFiberLane';
import {
GestureLane,
- includesBlockingLane,
- includesTransitionLane,
+ markRootEntangled,
+ markRootFinished,
+ NoLane,
+ NoLanes,
} from './ReactFiberLane';
-import {ensureRootIsScheduled} from './ReactFiberRootScheduler';
+import {
+ ensureRootIsScheduled,
+ requestTransitionLane,
+} from './ReactFiberRootScheduler';
import {getCurrentGestureOffset, stopViewTransition} from './ReactFiberConfig';
+import {pingGestureRoot, restartGestureRoot} from './ReactFiberWorkLoop';
// This type keeps track of any scheduled or active gestures.
export type ScheduledGesture = {
@@ -28,6 +35,8 @@ export type ScheduledGesture = {
rangeEnd: number, // The percentage along the timeline where the "destination" state is reached.
types: null | TransitionTypes, // Any addTransitionType call made during startGestureTransition.
running: null | RunningViewTransition, // Used to cancel the running transition after we're done.
+ committing: boolean, // If the gesture was released in a committed state and should actually commit.
+ revertLane: Lane, // The Lane that we'll use to schedule the revert.
prev: null | ScheduledGesture, // The previous scheduled gesture in the queue for this root.
next: null | ScheduledGesture, // The next scheduled gesture in the queue for this root.
};
@@ -55,6 +64,8 @@ export function scheduleGesture(
rangeEnd: 100, // Uninitialized
types: null,
running: null,
+ committing: false,
+ revertLane: NoLane, // Starts uninitialized.
prev: prev,
next: null,
};
@@ -120,79 +131,107 @@ export function cancelScheduledGesture(
root: FiberRoot,
gesture: ScheduledGesture,
): void {
+ // Entangle any Transitions started in this event with the revertLane of the gesture
+ // so that we commit them all together.
+ if (gesture.revertLane !== NoLane) {
+ const entangledLanes = gesture.revertLane | requestTransitionLane(null);
+ markRootEntangled(root, entangledLanes);
+ }
+
gesture.count--;
if (gesture.count === 0) {
- // Delete the scheduled gesture from the pending queue.
- deleteScheduledGesture(root, gesture);
+ // If the end state is closer to the end than the beginning then we commit into the
+ // end state before reverting back (or applying a new Transition).
+ // Otherwise we just revert back and don't commit.
+ let shouldCommit: boolean;
+ const finalOffset = getCurrentGestureOffset(gesture.provider);
+ const rangeStart = gesture.rangeStart;
+ const rangeEnd = gesture.rangeEnd;
+ if (rangeStart < rangeEnd) {
+ shouldCommit = finalOffset > rangeStart + (rangeEnd - rangeStart) / 2;
+ } else {
+ shouldCommit = finalOffset < rangeEnd + (rangeStart - rangeEnd) / 2;
+ }
// TODO: If we're currently rendering this gesture, we need to restart the render
// on a different gesture or cancel the render..
// TODO: We might want to pause the View Transition at this point since you should
// no longer be able to update the position of anything but it might be better to
// just commit the gesture state.
const runningTransition = gesture.running;
- if (runningTransition !== null) {
- const pendingLanesExcludingGestureLane = root.pendingLanes & ~GestureLane;
- if (
- includesBlockingLane(pendingLanesExcludingGestureLane) ||
- includesTransitionLane(pendingLanesExcludingGestureLane)
- ) {
- // If we have pending work we schedule the gesture to be stopped at the next commit.
- // This ensures that we don't snap back to the previous state until we have
- // had a chance to commit any resulting updates.
- const existing = root.stoppingGestures;
- if (existing !== null) {
- gesture.next = existing;
- existing.prev = gesture;
+ if (runningTransition !== null && shouldCommit) {
+ // If we are going to commit this gesture in its to state, we need to wait to
+ // stop it until it commits. We should now schedule a render at the gesture
+ // lane to actually commit it.
+ gesture.committing = true;
+ if (root.pendingGestures === gesture) {
+ // Ping the root given the new state. This is similar to pingSuspendedRoot.
+ // This will either schedule the gesture lane to be committed possibly from its current state.
+ pingGestureRoot(root);
+ }
+ } else {
+ // If we're not going to commit this gesture we can stop the View Transition
+ // right away and delete the scheduled gesture from the pending queue.
+ if (gesture.prev === null) {
+ if (root.pendingGestures === gesture) {
+ // This was the currently rendering gesture.
+ root.pendingGestures = gesture.next;
+ let remainingLanes = root.pendingLanes;
+ if (root.pendingGestures === null) {
+ // Gestures don't clear their lanes while the gesture is still active but it
+ // might not be scheduled to do any more renders and so we shouldn't schedule
+ // any more gesture lane work until a new gesture is scheduled.
+ remainingLanes &= ~GestureLane;
+ }
+ markRootFinished(
+ root,
+ GestureLane,
+ remainingLanes,
+ NoLane,
+ NoLane,
+ NoLanes,
+ );
+ // If we had a currently rendering gesture we need to now reset the gesture lane to
+ // now render the next gesture or cancel if there's no more gestures in the queue.
+ restartGestureRoot(root);
}
- root.stoppingGestures = gesture;
- } else {
gesture.running = null;
- // If there's no work scheduled so we can stop the View Transition right away.
- stopViewTransition(runningTransition);
+ if (runningTransition !== null) {
+ stopViewTransition(runningTransition);
+ }
+ } else {
+ // This was not the current gesture so it doesn't affect the current render.
+ gesture.prev.next = gesture.next;
+ if (gesture.next !== null) {
+ gesture.next.prev = gesture.prev;
+ }
+ gesture.prev = null;
+ gesture.next = null;
}
}
}
}
-export function deleteScheduledGesture(
- root: FiberRoot,
- gesture: ScheduledGesture,
-): void {
- if (gesture.prev === null) {
- if (root.pendingGestures === gesture) {
- root.pendingGestures = gesture.next;
- if (root.pendingGestures === null) {
- // Gestures don't clear their lanes while the gesture is still active but it
- // might not be scheduled to do any more renders and so we shouldn't schedule
- // any more gesture lane work until a new gesture is scheduled.
- root.pendingLanes &= ~GestureLane;
- }
- }
- if (root.stoppingGestures === gesture) {
- // This should not really happen the way we use it now but just in case we start.
- root.stoppingGestures = gesture.next;
+export function stopCommittedGesture(root: FiberRoot) {
+ // The top was just committed. We can delete it from the queue
+ // and stop its View Transition now.
+ const committedGesture = root.pendingGestures;
+ if (committedGesture !== null) {
+ // Mark it as no longer committing and should no longer be included in rerenders.
+ committedGesture.committing = false;
+ const nextGesture = committedGesture.next;
+ if (nextGesture === null) {
+ // Gestures don't clear their lanes while the gesture is still active but it
+ // might not be scheduled to do any more renders and so we shouldn't schedule
+ // any more gesture lane work until a new gesture is scheduled.
+ root.pendingLanes &= ~GestureLane;
+ } else {
+ nextGesture.prev = null;
}
- } else {
- gesture.prev.next = gesture.next;
- if (gesture.next !== null) {
- gesture.next.prev = gesture.prev;
- }
- gesture.prev = null;
- gesture.next = null;
- }
-}
-
-export function stopCompletedGestures(root: FiberRoot) {
- let gesture = root.stoppingGestures;
- root.stoppingGestures = null;
- while (gesture !== null) {
- if (gesture.running !== null) {
- stopViewTransition(gesture.running);
- gesture.running = null;
+ root.pendingGestures = nextGesture;
+ const runningTransition = committedGesture.running;
+ if (runningTransition !== null) {
+ committedGesture.running = null;
+ stopViewTransition(runningTransition);
}
- const nextGesture = gesture.next;
- gesture.next = null;
- gesture.prev = null;
- gesture = nextGesture;
}
}
diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js
index 66a390ebb81..422667c6a12 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.js
@@ -1384,7 +1384,7 @@ function updateReducerImpl(
// ScheduledGesture.
const scheduledGesture = update.gesture;
if (scheduledGesture !== null) {
- if (scheduledGesture.count === 0) {
+ if (scheduledGesture.count === 0 && !scheduledGesture.committing) {
// This gesture has already been cancelled. We can clean up this update.
update = update.next;
continue;
@@ -3792,7 +3792,14 @@ function dispatchOptimisticSetState(
if (provider !== null) {
// If this was a gesture, ensure we have a scheduled gesture and that
// we associate this update with this specific gesture instance.
- update.gesture = scheduleGesture(root, provider);
+ const gesture = (update.gesture = scheduleGesture(root, provider));
+ // Ensure the gesture always uses the same revert lane. This can happen for
+ // two startGestureTransition calls to the same provider in different events.
+ if (gesture.revertLane === NoLane) {
+ gesture.revertLane = update.revertLane;
+ } else {
+ update.revertLane = gesture.revertLane;
+ }
}
}
}
diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js
index 908893db948..26386597ee4 100644
--- a/packages/react-reconciler/src/ReactFiberRoot.js
+++ b/packages/react-reconciler/src/ReactFiberRoot.js
@@ -115,7 +115,6 @@ function FiberRootNode(
if (enableGestureTransition) {
this.pendingGestures = null;
- this.stoppingGestures = null;
this.gestureClone = null;
}
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js
index f7200458be1..4fcf936f1cb 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js
@@ -395,10 +395,7 @@ import {
} from './ReactFiberRootScheduler';
import {getMaskedContext, getUnmaskedContext} from './ReactFiberLegacyContext';
import {logUncaughtError} from './ReactFiberErrorLogger';
-import {
- deleteScheduledGesture,
- stopCompletedGestures,
-} from './ReactFiberGestureScheduler';
+import {stopCommittedGesture} from './ReactFiberGestureScheduler';
import {claimQueuedTransitionTypes} from './ReactFiberTransitionTypes';
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
@@ -1404,7 +1401,7 @@ function finishConcurrentRender(
if (shouldForceFlushFallbacksInDEV()) {
// We're inside an `act` scope. Commit immediately.
- commitRoot(
+ completeRoot(
root,
finishedWork,
lanes,
@@ -1414,6 +1411,7 @@ function finishConcurrentRender(
workInProgressDeferredLane,
workInProgressRootInterleavedUpdatedLanes,
workInProgressSuspendedRetryLanes,
+ workInProgressRootDidSkipSuspendedSiblings,
exitStatus,
null,
null,
@@ -1455,7 +1453,7 @@ function finishConcurrentRender(
// run one after the other.
pendingEffectsLanes = lanes;
root.timeoutHandle = scheduleTimeout(
- commitRootWhenReady.bind(
+ completeRootWhenReady.bind(
null,
root,
finishedWork,
@@ -1477,7 +1475,7 @@ function finishConcurrentRender(
return;
}
}
- commitRootWhenReady(
+ completeRootWhenReady(
root,
finishedWork,
workInProgressRootRecoverableErrors,
@@ -1496,7 +1494,7 @@ function finishConcurrentRender(
}
}
-function commitRootWhenReady(
+function completeRootWhenReady(
root: FiberRoot,
finishedWork: Fiber,
recoverableErrors: Array> | null,
@@ -1537,12 +1535,18 @@ function commitRootWhenReady(
// This will also track any newly added or appearing ViewTransition
// components for the purposes of forming pairs.
accumulateSuspenseyCommit(finishedWork, lanes, suspendedState);
- if (isViewTransitionEligible || isGestureTransition) {
- // If we're stopping gestures we don't have to wait for any pending
- // view transition. We'll stop it when we commit.
- if (!enableGestureTransition || root.stoppingGestures === null) {
- suspendOnActiveViewTransition(suspendedState, root.containerInfo);
- }
+ if (
+ isViewTransitionEligible ||
+ (isGestureTransition &&
+ root.pendingGestures !== null &&
+ // If we're committing this gesture and it already has a View Transition
+ // running, then we don't have to wait for that gesture. We'll stop it
+ // when we commit.
+ (root.pendingGestures.running === null ||
+ !root.pendingGestures.committing))
+ ) {
+ // Wait for any pending View Transition (including gestures) to finish.
+ suspendOnActiveViewTransition(suspendedState, root.containerInfo);
}
// For timeouts we use the previous fallback commit for retries and
// the start time of the transition for transitions. This offset
@@ -1569,7 +1573,7 @@ function commitRootWhenReady(
// root again.
pendingEffectsLanes = lanes;
root.cancelPendingCommit = schedulePendingCommit(
- commitRoot.bind(
+ completeRoot.bind(
null,
root,
finishedWork,
@@ -1580,6 +1584,7 @@ function commitRootWhenReady(
spawnedLane,
updatedLanes,
suspendedRetryLanes,
+ didSkipSuspendedSiblings,
exitStatus,
suspendedState,
enableProfilerTimer
@@ -1596,7 +1601,7 @@ function commitRootWhenReady(
}
// Otherwise, commit immediately.;
- commitRoot(
+ completeRoot(
root,
finishedWork,
lanes,
@@ -1606,6 +1611,7 @@ function commitRootWhenReady(
spawnedLane,
updatedLanes,
suspendedRetryLanes,
+ didSkipSuspendedSiblings,
exitStatus,
suspendedState,
suspendedCommitReason,
@@ -3413,7 +3419,7 @@ function unwindUnitOfWork(unitOfWork: Fiber, skipSiblings: boolean): void {
workInProgress = null;
}
-function commitRoot(
+function completeRoot(
root: FiberRoot,
finishedWork: null | Fiber,
lanes: Lanes,
@@ -3423,6 +3429,7 @@ function commitRoot(
spawnedLane: Lane,
updatedLanes: Lanes,
suspendedRetryLanes: Lanes,
+ didSkipSuspendedSiblings: boolean,
exitStatus: RootExitStatus,
suspendedState: null | SuspendedState,
suspendedCommitReason: SuspendedCommitReason, // Profiling-only
@@ -3489,9 +3496,9 @@ function commitRoot(
markCommitStopped();
}
if (enableGestureTransition) {
- // Stop any gestures that were completed and is now being reverted.
- if (root.stoppingGestures !== null) {
- stopCompletedGestures(root);
+ // Stop any gestures that were committed.
+ if (isGestureRender(lanes)) {
+ stopCommittedGesture(root);
}
}
return;
@@ -3513,10 +3520,95 @@ function commitRoot(
);
}
+ if (root === workInProgressRoot) {
+ // We can reset these now that they are finished.
+ workInProgressRoot = null;
+ workInProgress = null;
+ workInProgressRootRenderLanes = NoLanes;
+ } else {
+ // This indicates that the last root we worked on is not the same one that
+ // we're committing now. This most commonly happens when a suspended root
+ // times out.
+ }
+
+ // workInProgressX might be overwritten, so we want
+ // to store it in pendingPassiveX until they get processed
+ // We need to pass this through as an argument to completeRoot
+ // because workInProgressX might have changed between
+ // the previous render and commit if we throttle the commit
+ // with setTimeout
+ pendingFinishedWork = finishedWork;
+ pendingEffectsRoot = root;
+ pendingEffectsLanes = lanes;
+ pendingPassiveTransitions = transitions;
+ pendingRecoverableErrors = recoverableErrors;
+ pendingDidIncludeRenderPhaseUpdate = didIncludeRenderPhaseUpdate;
+ if (enableProfilerTimer) {
+ pendingEffectsRenderEndTime = completedRenderEndTime;
+ pendingSuspendedCommitReason = suspendedCommitReason;
+ pendingDelayedCommitReason = IMMEDIATE_COMMIT;
+ pendingSuspendedViewTransitionReason = null;
+ }
+
+ if (enableGestureTransition && isGestureRender(lanes)) {
+ const committingGesture = root.pendingGestures;
+ if (committingGesture !== null && !committingGesture.committing) {
+ // This gesture is not ready to commit yet. We'll mark it as suspended and
+ // start a gesture transition which isn't really a side-effect. Then later
+ // we might come back around to actually committing the root.
+ const didAttemptEntireTree = !didSkipSuspendedSiblings;
+ markRootSuspended(root, lanes, spawnedLane, didAttemptEntireTree);
+ if (committingGesture.running === null) {
+ applyGestureOnRoot(
+ root,
+ finishedWork,
+ recoverableErrors,
+ suspendedState,
+ enableProfilerTimer
+ ? suspendedCommitReason === null
+ ? completedRenderEndTime
+ : commitStartTime
+ : 0,
+ );
+ } else {
+ // If we already have a gesture running, we don't update it in place
+ // even if we have a new tree. Instead we wait until we can commit.
+ }
+ return;
+ }
+ }
+
+ // If we're not starting a gesture we now actually commit the root.
+ commitRoot(
+ root,
+ finishedWork,
+ lanes,
+ spawnedLane,
+ updatedLanes,
+ suspendedRetryLanes,
+ suspendedState,
+ suspendedCommitReason,
+ completedRenderEndTime,
+ );
+}
+
+function commitRoot(
+ root: FiberRoot,
+ finishedWork: Fiber,
+ lanes: Lanes,
+ spawnedLane: Lane,
+ updatedLanes: Lanes,
+ suspendedRetryLanes: Lanes,
+ suspendedState: null | SuspendedState,
+ suspendedCommitReason: SuspendedCommitReason, // Profiling-only
+ completedRenderEndTime: number, // Profiling-only
+) {
// Check which lanes no longer have any work scheduled on them, and mark
// those as finished.
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
+ pendingEffectsRemainingLanes = remainingLanes;
+
// Make sure to account for lanes that were updated by a concurrent event
// during the render phase; don't mark them as finished.
const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
@@ -3546,53 +3638,6 @@ function commitRoot(
// Reset this before firing side effects so we can detect recursive updates.
didIncludeCommitPhaseUpdate = false;
- if (root === workInProgressRoot) {
- // We can reset these now that they are finished.
- workInProgressRoot = null;
- workInProgress = null;
- workInProgressRootRenderLanes = NoLanes;
- } else {
- // This indicates that the last root we worked on is not the same one that
- // we're committing now. This most commonly happens when a suspended root
- // times out.
- }
-
- // workInProgressX might be overwritten, so we want
- // to store it in pendingPassiveX until they get processed
- // We need to pass this through as an argument to commitRoot
- // because workInProgressX might have changed between
- // the previous render and commit if we throttle the commit
- // with setTimeout
- pendingFinishedWork = finishedWork;
- pendingEffectsRoot = root;
- pendingEffectsLanes = lanes;
- pendingEffectsRemainingLanes = remainingLanes;
- pendingPassiveTransitions = transitions;
- pendingRecoverableErrors = recoverableErrors;
- pendingDidIncludeRenderPhaseUpdate = didIncludeRenderPhaseUpdate;
- if (enableProfilerTimer) {
- pendingEffectsRenderEndTime = completedRenderEndTime;
- pendingSuspendedCommitReason = suspendedCommitReason;
- pendingDelayedCommitReason = IMMEDIATE_COMMIT;
- pendingSuspendedViewTransitionReason = null;
- }
-
- if (enableGestureTransition && isGestureRender(lanes)) {
- // This is a special kind of render that doesn't commit regular effects.
- commitGestureOnRoot(
- root,
- finishedWork,
- recoverableErrors,
- suspendedState,
- enableProfilerTimer
- ? suspendedCommitReason === null
- ? completedRenderEndTime
- : commitStartTime
- : 0,
- );
- return;
- }
-
// If there are pending passive effects, schedule a callback to process them.
// Do this as early as possible, so it is queued before anything else that
// might get scheduled in the commit phase. (See #16714.)
@@ -3705,20 +3750,19 @@ function commitRoot(
}
}
- let willStartViewTransition = shouldStartViewTransition;
if (enableGestureTransition) {
- // Stop any gestures that were completed and is now being committed.
- if (root.stoppingGestures !== null) {
- stopCompletedGestures(root);
- // If we are in the process of stopping some gesture we shouldn't start
- // a View Transition because that would start from the previous state to
- // the next state.
- willStartViewTransition = false;
+ // Stop any gestures that were committed.
+ if (isGestureRender(lanes)) {
+ stopCommittedGesture(root);
+ // Note that shouldStartViewTransition should always be false here because
+ // committing a gesture never starts a new View Transition itself since it's
+ // not a View Transition eligible lane. Only follow up Transition commits can
+ // cause animate.
}
}
pendingEffectsStatus = PENDING_MUTATION_PHASE;
- if (enableViewTransition && willStartViewTransition) {
+ if (enableViewTransition && shouldStartViewTransition) {
if (enableProfilerTimer && enableComponentPerformanceTrack) {
startAnimating(lanes);
}
@@ -4148,7 +4192,7 @@ function flushSpawnedWork(): void {
flushPendingEffects();
}
- // Always call this before exiting `commitRoot`, to ensure that any
+ // Always call this before exiting `completeRoot`, to ensure that any
// additional work on this root is scheduled.
ensureRootIsScheduled(root);
@@ -4237,7 +4281,7 @@ function flushSpawnedWork(): void {
}
}
-function commitGestureOnRoot(
+function applyGestureOnRoot(
root: FiberRoot,
finishedWork: Fiber,
recoverableErrors: null | Array>,
@@ -4252,7 +4296,6 @@ function commitGestureOnRoot(
ensureRootIsScheduled(root);
return;
}
- deleteScheduledGesture(root, finishedGesture);
if (enableProfilerTimer && enableComponentPerformanceTrack) {
startAnimating(pendingEffectsLanes);
@@ -4461,7 +4504,7 @@ function flushPassiveEffects(): boolean {
// flushPassiveEffectsImpl
const root = pendingEffectsRoot;
// Cache and clear the remaining lanes flag; it must be reset since this
- // method can be called from various places, not always from commitRoot
+ // method can be called from various places, not always from completeRoot
// where the remaining lanes are known
const remainingLanes = pendingEffectsRemainingLanes;
pendingEffectsRemainingLanes = NoLanes;
@@ -4849,6 +4892,57 @@ function pingSuspendedRoot(
ensureRootIsScheduled(root);
}
+export function pingGestureRoot(root: FiberRoot): void {
+ const gesture = root.pendingGestures;
+ if (gesture === null) {
+ return;
+ }
+ if (
+ root.cancelPendingCommit !== null &&
+ isGestureRender(pendingEffectsLanes)
+ ) {
+ // We have a suspended commit which we'll discard and rerender.
+ // TODO: Just use this commit since it's ready to go.
+ const cancelPendingCommit = root.cancelPendingCommit;
+ if (cancelPendingCommit !== null) {
+ root.cancelPendingCommit = null;
+ cancelPendingCommit();
+ }
+ }
+ // Ping it for rerender and commit.
+ markRootPinged(root, GestureLane);
+ ensureRootIsScheduled(root);
+}
+
+export function restartGestureRoot(root: FiberRoot): void {
+ if (
+ workInProgressRoot === root &&
+ isGestureRender(workInProgressRootRenderLanes)
+ ) {
+ // The current render was a gesture but it's now defunct. We need to restart the render.
+ if ((executionContext & RenderContext) === NoContext) {
+ prepareFreshStack(root, NoLanes);
+ } else {
+ // TODO: Throw interruption when supported again.
+ }
+ } else if (
+ root.cancelPendingCommit !== null &&
+ isGestureRender(pendingEffectsLanes)
+ ) {
+ // We have a suspended commit which we'll discard.
+ const cancelPendingCommit = root.cancelPendingCommit;
+ if (cancelPendingCommit !== null) {
+ root.cancelPendingCommit = null;
+ cancelPendingCommit();
+ }
+ }
+ if (root.pendingGestures !== null) {
+ // We still have gestures to work on. Let's schedule a restart.
+ markRootPinged(root, GestureLane);
+ }
+ ensureRootIsScheduled(root);
+}
+
function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
// The boundary fiber (a Suspense component or SuspenseList component)
// previously was rendered in its fallback state. One of the promises that
diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js
index 775b69d211f..ce22050123c 100644
--- a/packages/react-reconciler/src/ReactInternalTypes.js
+++ b/packages/react-reconciler/src/ReactInternalTypes.js
@@ -292,7 +292,6 @@ type BaseFiberRootProperties = {
transitionTypes: null | TransitionTypes, // TODO: Make this a LaneMap.
// enableGestureTransition only
pendingGestures: null | ScheduledGesture,
- stoppingGestures: null | ScheduledGesture,
gestureClone: null | Instance,
};