mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
moved mutation code to passive (#24251)
This PR moves the code for transition tracing in the mutation phase that adds transitions to the pending callbacks object (to be called sometime later after paint) from the mutation to the passive phase. Things to think about: Passive effects can be flushed before or after paint. How do we make sure that we get the correct end time for the interaction?
This commit is contained in:
@@ -2227,32 +2227,6 @@ function commitMutationEffectsOnFiber(
|
||||
// because of the shared reconciliation logic below.
|
||||
const flags = finishedWork.flags;
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
switch (finishedWork.tag) {
|
||||
case HostRoot: {
|
||||
const state = finishedWork.memoizedState;
|
||||
const transitions = state.transitions;
|
||||
if (transitions !== null) {
|
||||
transitions.forEach(transition => {
|
||||
// TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
|
||||
addTransitionStartCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
|
||||
addTransitionCompleteCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
});
|
||||
|
||||
clearTransitionsForLanes(root, lanes);
|
||||
state.transitions = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & ContentReset) {
|
||||
commitResetTextContent(finishedWork);
|
||||
}
|
||||
@@ -2639,12 +2613,17 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
export function commitPassiveMountEffects(
|
||||
root: FiberRoot,
|
||||
finishedWork: Fiber,
|
||||
committedLanes: Lanes,
|
||||
): void {
|
||||
nextEffect = finishedWork;
|
||||
commitPassiveMountEffects_begin(finishedWork, root);
|
||||
commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
|
||||
}
|
||||
|
||||
function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
function commitPassiveMountEffects_begin(
|
||||
subtreeRoot: Fiber,
|
||||
root: FiberRoot,
|
||||
committedLanes: Lanes,
|
||||
) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
@@ -2652,7 +2631,7 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
ensureCorrectReturnPointer(firstChild, fiber);
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
commitPassiveMountEffects_complete(subtreeRoot, root);
|
||||
commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2660,13 +2639,15 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
function commitPassiveMountEffects_complete(
|
||||
subtreeRoot: Fiber,
|
||||
root: FiberRoot,
|
||||
committedLanes: Lanes,
|
||||
) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
|
||||
if ((fiber.flags & Passive) !== NoFlags) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
try {
|
||||
commitPassiveMountOnFiber(root, fiber);
|
||||
commitPassiveMountOnFiber(root, fiber, committedLanes);
|
||||
} catch (error) {
|
||||
reportUncaughtErrorInDEV(error);
|
||||
captureCommitPhaseError(fiber, fiber.return, error);
|
||||
@@ -2693,6 +2674,7 @@ function commitPassiveMountEffects_complete(
|
||||
function commitPassiveMountOnFiber(
|
||||
finishedRoot: FiberRoot,
|
||||
finishedWork: Fiber,
|
||||
committedLanes: Lanes,
|
||||
): void {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
@@ -2734,6 +2716,27 @@ function commitPassiveMountOnFiber(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const transitions = finishedWork.memoizedState.transitions;
|
||||
if (transitions !== null) {
|
||||
transitions.forEach(transition => {
|
||||
// TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
|
||||
addTransitionStartCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
|
||||
addTransitionCompleteCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
});
|
||||
|
||||
clearTransitionsForLanes(finishedRoot, committedLanes);
|
||||
finishedWork.memoizedState.transitions = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LegacyHiddenComponent:
|
||||
|
||||
@@ -2227,32 +2227,6 @@ function commitMutationEffectsOnFiber(
|
||||
// because of the shared reconciliation logic below.
|
||||
const flags = finishedWork.flags;
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
switch (finishedWork.tag) {
|
||||
case HostRoot: {
|
||||
const state = finishedWork.memoizedState;
|
||||
const transitions = state.transitions;
|
||||
if (transitions !== null) {
|
||||
transitions.forEach(transition => {
|
||||
// TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
|
||||
addTransitionStartCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
|
||||
addTransitionCompleteCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
});
|
||||
|
||||
clearTransitionsForLanes(root, lanes);
|
||||
state.transitions = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & ContentReset) {
|
||||
commitResetTextContent(finishedWork);
|
||||
}
|
||||
@@ -2639,12 +2613,17 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
export function commitPassiveMountEffects(
|
||||
root: FiberRoot,
|
||||
finishedWork: Fiber,
|
||||
committedLanes: Lanes,
|
||||
): void {
|
||||
nextEffect = finishedWork;
|
||||
commitPassiveMountEffects_begin(finishedWork, root);
|
||||
commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
|
||||
}
|
||||
|
||||
function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
function commitPassiveMountEffects_begin(
|
||||
subtreeRoot: Fiber,
|
||||
root: FiberRoot,
|
||||
committedLanes: Lanes,
|
||||
) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
@@ -2652,7 +2631,7 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
ensureCorrectReturnPointer(firstChild, fiber);
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
commitPassiveMountEffects_complete(subtreeRoot, root);
|
||||
commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2660,13 +2639,15 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
|
||||
function commitPassiveMountEffects_complete(
|
||||
subtreeRoot: Fiber,
|
||||
root: FiberRoot,
|
||||
committedLanes: Lanes,
|
||||
) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
|
||||
if ((fiber.flags & Passive) !== NoFlags) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
try {
|
||||
commitPassiveMountOnFiber(root, fiber);
|
||||
commitPassiveMountOnFiber(root, fiber, committedLanes);
|
||||
} catch (error) {
|
||||
reportUncaughtErrorInDEV(error);
|
||||
captureCommitPhaseError(fiber, fiber.return, error);
|
||||
@@ -2693,6 +2674,7 @@ function commitPassiveMountEffects_complete(
|
||||
function commitPassiveMountOnFiber(
|
||||
finishedRoot: FiberRoot,
|
||||
finishedWork: Fiber,
|
||||
committedLanes: Lanes,
|
||||
): void {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
@@ -2734,6 +2716,27 @@ function commitPassiveMountOnFiber(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const transitions = finishedWork.memoizedState.transitions;
|
||||
if (transitions !== null) {
|
||||
transitions.forEach(transition => {
|
||||
// TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
|
||||
addTransitionStartCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
|
||||
addTransitionCompleteCallbackToPendingTransition({
|
||||
transitionName: transition.name,
|
||||
startTime: transition.startTime,
|
||||
});
|
||||
});
|
||||
|
||||
clearTransitionsForLanes(finishedRoot, committedLanes);
|
||||
finishedWork.memoizedState.transitions = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LegacyHiddenComponent:
|
||||
|
||||
@@ -153,6 +153,7 @@ import {
|
||||
popRenderLanes,
|
||||
getRenderTargetTime,
|
||||
subtreeRenderLanes,
|
||||
getWorkInProgressTransitions,
|
||||
} from './ReactFiberWorkLoop.new';
|
||||
import {
|
||||
OffscreenLane,
|
||||
@@ -862,6 +863,17 @@ function completeWork(
|
||||
}
|
||||
case HostRoot: {
|
||||
const fiberRoot = (workInProgress.stateNode: FiberRoot);
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const transitions = getWorkInProgressTransitions();
|
||||
// We set the Passive flag here because if there are new transitions,
|
||||
// we will need to schedule callbacks and process the transitions,
|
||||
// which we do in the passive phase
|
||||
if (transitions !== null) {
|
||||
workInProgress.flags |= Passive;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableCache) {
|
||||
popRootTransition(fiberRoot, renderLanes);
|
||||
|
||||
@@ -918,6 +930,14 @@ function completeWork(
|
||||
}
|
||||
updateHostContainer(current, workInProgress);
|
||||
bubbleProperties(workInProgress);
|
||||
if (enableTransitionTracing) {
|
||||
if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
|
||||
// If any of our suspense children toggle visibility, this means that
|
||||
// the pending boundaries array needs to be updated, which we only
|
||||
// do in the passive phase.
|
||||
workInProgress.flags |= Passive;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case HostComponent: {
|
||||
@@ -1187,6 +1207,12 @@ function completeWork(
|
||||
const offscreenFiber: Fiber = (workInProgress.child: any);
|
||||
offscreenFiber.flags |= Visibility;
|
||||
|
||||
// If the suspended state of the boundary changes, we need to schedule
|
||||
// a passive effect, which is when we process the transitions
|
||||
if (enableTransitionTracing) {
|
||||
offscreenFiber.flags |= Passive;
|
||||
}
|
||||
|
||||
// TODO: This will still suspend a synchronous tree if anything
|
||||
// in the concurrent tree already suspended during this render.
|
||||
// This is a known bug.
|
||||
|
||||
@@ -153,6 +153,7 @@ import {
|
||||
popRenderLanes,
|
||||
getRenderTargetTime,
|
||||
subtreeRenderLanes,
|
||||
getWorkInProgressTransitions,
|
||||
} from './ReactFiberWorkLoop.old';
|
||||
import {
|
||||
OffscreenLane,
|
||||
@@ -862,6 +863,17 @@ function completeWork(
|
||||
}
|
||||
case HostRoot: {
|
||||
const fiberRoot = (workInProgress.stateNode: FiberRoot);
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const transitions = getWorkInProgressTransitions();
|
||||
// We set the Passive flag here because if there are new transitions,
|
||||
// we will need to schedule callbacks and process the transitions,
|
||||
// which we do in the passive phase
|
||||
if (transitions !== null) {
|
||||
workInProgress.flags |= Passive;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableCache) {
|
||||
popRootTransition(fiberRoot, renderLanes);
|
||||
|
||||
@@ -918,6 +930,14 @@ function completeWork(
|
||||
}
|
||||
updateHostContainer(current, workInProgress);
|
||||
bubbleProperties(workInProgress);
|
||||
if (enableTransitionTracing) {
|
||||
if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
|
||||
// If any of our suspense children toggle visibility, this means that
|
||||
// the pending boundaries array needs to be updated, which we only
|
||||
// do in the passive phase.
|
||||
workInProgress.flags |= Passive;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case HostComponent: {
|
||||
@@ -1187,6 +1207,12 @@ function completeWork(
|
||||
const offscreenFiber: Fiber = (workInProgress.child: any);
|
||||
offscreenFiber.flags |= Visibility;
|
||||
|
||||
// If the suspended state of the boundary changes, we need to schedule
|
||||
// a passive effect, which is when we process the transitions
|
||||
if (enableTransitionTracing) {
|
||||
offscreenFiber.flags |= Passive;
|
||||
}
|
||||
|
||||
// TODO: This will still suspend a synchronous tree if anything
|
||||
// in the concurrent tree already suspended during this render.
|
||||
// This is a known bug.
|
||||
|
||||
@@ -2322,27 +2322,6 @@ function commitRootImpl(
|
||||
// If layout work was scheduled, flush it now.
|
||||
flushSyncCallbacks();
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
|
||||
const prevRootTransitionCallbacks = root.transitionCallbacks;
|
||||
if (
|
||||
prevPendingTransitionCallbacks !== null &&
|
||||
prevRootTransitionCallbacks !== null
|
||||
) {
|
||||
// TODO(luna) Refactor this code into the Host Config
|
||||
const endTime = now();
|
||||
currentPendingTransitionCallbacks = null;
|
||||
|
||||
scheduleCallback(IdleSchedulerPriority, () =>
|
||||
processTransitionCallbacks(
|
||||
prevPendingTransitionCallbacks,
|
||||
endTime,
|
||||
prevRootTransitionCallbacks,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
@@ -2457,7 +2436,7 @@ function flushPassiveEffectsImpl() {
|
||||
executionContext |= CommitContext;
|
||||
|
||||
commitPassiveUnmountEffects(root.current);
|
||||
commitPassiveMountEffects(root, root.current);
|
||||
commitPassiveMountEffects(root, root.current, lanes);
|
||||
|
||||
// TODO: Move to commitPassiveMountEffects
|
||||
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
||||
@@ -2487,6 +2466,34 @@ function flushPassiveEffectsImpl() {
|
||||
|
||||
flushSyncCallbacks();
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
|
||||
const prevRootTransitionCallbacks = root.transitionCallbacks;
|
||||
if (
|
||||
prevPendingTransitionCallbacks !== null &&
|
||||
prevRootTransitionCallbacks !== null
|
||||
) {
|
||||
// TODO(luna) Refactor this code into the Host Config
|
||||
// TODO(luna) The end time here is not necessarily accurate
|
||||
// because passive effects could be called before paint
|
||||
// (synchronously) or after paint (normally). We need
|
||||
// to come up with a way to get the correct end time for both cases.
|
||||
// One solution is in the host config, if the passive effects
|
||||
// have not yet been run, make a call to flush the passive effects
|
||||
// right after paint.
|
||||
const endTime = now();
|
||||
currentPendingTransitionCallbacks = null;
|
||||
|
||||
scheduleCallback(IdleSchedulerPriority, () =>
|
||||
processTransitionCallbacks(
|
||||
prevPendingTransitionCallbacks,
|
||||
endTime,
|
||||
prevRootTransitionCallbacks,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// If additional passive effects were scheduled, increment a counter. If this
|
||||
// exceeds the limit, we'll fire a warning.
|
||||
|
||||
@@ -2322,27 +2322,6 @@ function commitRootImpl(
|
||||
// If layout work was scheduled, flush it now.
|
||||
flushSyncCallbacks();
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
|
||||
const prevRootTransitionCallbacks = root.transitionCallbacks;
|
||||
if (
|
||||
prevPendingTransitionCallbacks !== null &&
|
||||
prevRootTransitionCallbacks !== null
|
||||
) {
|
||||
// TODO(luna) Refactor this code into the Host Config
|
||||
const endTime = now();
|
||||
currentPendingTransitionCallbacks = null;
|
||||
|
||||
scheduleCallback(IdleSchedulerPriority, () =>
|
||||
processTransitionCallbacks(
|
||||
prevPendingTransitionCallbacks,
|
||||
endTime,
|
||||
prevRootTransitionCallbacks,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStopped();
|
||||
@@ -2457,7 +2436,7 @@ function flushPassiveEffectsImpl() {
|
||||
executionContext |= CommitContext;
|
||||
|
||||
commitPassiveUnmountEffects(root.current);
|
||||
commitPassiveMountEffects(root, root.current);
|
||||
commitPassiveMountEffects(root, root.current, lanes);
|
||||
|
||||
// TODO: Move to commitPassiveMountEffects
|
||||
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
||||
@@ -2487,6 +2466,34 @@ function flushPassiveEffectsImpl() {
|
||||
|
||||
flushSyncCallbacks();
|
||||
|
||||
if (enableTransitionTracing) {
|
||||
const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
|
||||
const prevRootTransitionCallbacks = root.transitionCallbacks;
|
||||
if (
|
||||
prevPendingTransitionCallbacks !== null &&
|
||||
prevRootTransitionCallbacks !== null
|
||||
) {
|
||||
// TODO(luna) Refactor this code into the Host Config
|
||||
// TODO(luna) The end time here is not necessarily accurate
|
||||
// because passive effects could be called before paint
|
||||
// (synchronously) or after paint (normally). We need
|
||||
// to come up with a way to get the correct end time for both cases.
|
||||
// One solution is in the host config, if the passive effects
|
||||
// have not yet been run, make a call to flush the passive effects
|
||||
// right after paint.
|
||||
const endTime = now();
|
||||
currentPendingTransitionCallbacks = null;
|
||||
|
||||
scheduleCallback(IdleSchedulerPriority, () =>
|
||||
processTransitionCallbacks(
|
||||
prevPendingTransitionCallbacks,
|
||||
endTime,
|
||||
prevRootTransitionCallbacks,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// If additional passive effects were scheduled, increment a counter. If this
|
||||
// exceeds the limit, we'll fire a warning.
|
||||
|
||||
Reference in New Issue
Block a user