40
disableDefaultPropsExceptForClasses,
41
enableSiblingPrerendering,
42
enableComponentPerformanceTrack,
43
+ enableYieldingBeforePassive,
44
} from 'shared/ReactFeatureFlags';
45
import ReactSharedInternals from 'shared/ReactSharedInternals';
46
import is from 'shared/objectIs';
611
612
let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
613
613
-let rootDoesHavePassiveEffects: boolean = false;
614
let rootWithPendingPassiveEffects: FiberRoot | null = null;
615
let pendingPassiveEffectsLanes: Lanes = NoLanes;
616
let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes;
638
return workInProgressRootRenderLanes;
639
}
640
641
+export function getRootWithPendingPassiveEffects(): FiberRoot | null {
642
+ return rootWithPendingPassiveEffects;
643
+}
644
+
645
+export function getPendingPassiveEffectsLanes(): Lanes {
646
+ return pendingPassiveEffectsLanes;
647
+}
648
+
649
export function isWorkLoopSuspendedOnData(): boolean {
650
return (
651
workInProgressSuspendedReason === SuspendedOnData ||
3218
);
3219
}
3220
3213
- // commitRoot never returns a continuation; it always finishes synchronously.
3214
- // So we can clear these now to allow a new callback to be scheduled.
3215
- root.callbackNode = null;
3216
- root.callbackPriority = NoLane;
3217
- root.cancelPendingCommit = null;
3218
-
3221
// Check which lanes no longer have any work scheduled on them, and mark
3222
// those as finished.
3223
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
3255
// might get scheduled in the commit phase. (See #16714.)
3256
// TODO: Delete all other places that schedule the passive effect callback
3257
// They're redundant.
3258
+ let rootDoesHavePassiveEffects: boolean = false;
3259
if (
3260
// If this subtree rendered with profiling this commit, we need to visit it to log it.
3261
(enableProfilerTimer &&
3264
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
3265
(finishedWork.flags & PassiveMask) !== NoFlags
3266
) {
3264
- if (!rootDoesHavePassiveEffects) {
3265
- rootDoesHavePassiveEffects = true;
3266
- pendingPassiveEffectsRemainingLanes = remainingLanes;
3267
- pendingPassiveEffectsRenderEndTime = completedRenderEndTime;
3268
- // workInProgressTransitions might be overwritten, so we want
3269
- // to store it in pendingPassiveTransitions until they get processed
3270
- // We need to pass this through as an argument to commitRoot
3271
- // because workInProgressTransitions might have changed between
3272
- // the previous render and commit if we throttle the commit
3273
- // with setTimeout
3274
- pendingPassiveTransitions = transitions;
3267
+ rootDoesHavePassiveEffects = true;
3268
+ pendingPassiveEffectsRemainingLanes = remainingLanes;
3269
+ pendingPassiveEffectsRenderEndTime = completedRenderEndTime;
3270
+ // workInProgressTransitions might be overwritten, so we want
3271
+ // to store it in pendingPassiveTransitions until they get processed
3272
+ // We need to pass this through as an argument to commitRoot
3273
+ // because workInProgressTransitions might have changed between
3274
+ // the previous render and commit if we throttle the commit
3275
+ // with setTimeout
3276
+ pendingPassiveTransitions = transitions;
3277
+ if (enableYieldingBeforePassive) {
3278
+ // We don't schedule a separate task for flushing passive effects.
3279
+ // Instead, we just rely on ensureRootIsScheduled below to schedule
3280
+ // a callback for us to flush the passive effects.
3281
+ } else {
3282
+ // So we can clear these now to allow a new callback to be scheduled.
3283
+ root.callbackNode = null;
3284
+ root.callbackPriority = NoLane;
3285
+ root.cancelPendingCommit = null;
3286
scheduleCallback(NormalSchedulerPriority, () => {
3287
if (enableProfilerTimer && enableComponentPerformanceTrack) {
3288
// Track the currently executing event if there is one so we can ignore this
3296
return null;
3297
});
3298
}
3299
+ } else {
3300
+ // If we don't have passive effects, we're not going to need to perform more work
3301
+ // so we can clear the callback now.
3302
+ root.callbackNode = null;
3303
+ root.callbackPriority = NoLane;
3304
+ root.cancelPendingCommit = null;
3305
}
3306
3307
if (enableProfilerTimer) {
3458
onCommitRootTestSelector();
3459
}
3460
3444
- // Always call this before exiting `commitRoot`, to ensure that any
3445
- // additional work on this root is scheduled.
3446
- ensureRootIsScheduled(root);
3447
-
3461
if (recoverableErrors !== null) {
3462
// There were errors during this render, but recovered from them without
3463
// needing to surface it to the UI. We log them here.
3493
flushPassiveEffects();
3494
}
3495
3496
+ // Always call this before exiting `commitRoot`, to ensure that any
3497
+ // additional work on this root is scheduled.
3498
+ ensureRootIsScheduled(root);
3499
+
3500
// Read this again, since a passive effect might have updated it
3501
remainingLanes = root.pendingLanes;
3502
3665
// because it's only used for profiling), but it's a refactor hazard.
3666
pendingPassiveEffectsLanes = NoLanes;
3667
3668
+ if (enableYieldingBeforePassive) {
3669
+ // We've finished our work for this render pass.
3670
+ root.callbackNode = null;
3671
+ root.callbackPriority = NoLane;
3672
+ root.cancelPendingCommit = null;
3673
+ }
3674
+
3675
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
3676
throw new Error('Cannot flush passive effects while already rendering.');
3677
}
3769
didScheduleUpdateDuringPassiveEffects = false;
3770
}
3771
3772
+ if (enableYieldingBeforePassive) {
3773
+ // Next, we reschedule any remaining work in a new task since it's a new
3774
+ // sequence of work. We wait until the end to do this in case the passive
3775
+ // effect schedules higher priority work than we had remaining. That way
3776
+ // we don't schedule an early callback that gets cancelled anyway.
3777
+ ensureRootIsScheduled(root);
3778
+ }
3779
+
3780
// TODO: Move to commitPassiveMountEffects
3781
onPostCommitRootDevTools(root);
3782
if (enableProfilerTimer && enableProfilerCommitHooks) {