diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js
index 3775232f196b9..e02ae2d5f8f92 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js
@@ -1819,7 +1819,8 @@ function commitRootImpl(root, renderPriorityLevel) {
function commitBeforeMutationEffects() {
while (nextEffect !== null) {
- if ((nextEffect.effectTag & Snapshot) !== NoEffect) {
+ const effectTag = nextEffect.effectTag;
+ if ((effectTag & Snapshot) !== NoEffect) {
setCurrentDebugFiberInDEV(nextEffect);
recordEffect();
@@ -1828,6 +1829,17 @@ function commitBeforeMutationEffects() {
resetCurrentDebugFiberInDEV();
}
+ if ((effectTag & Passive) !== NoEffect) {
+ // If there are passive effects, schedule a callback to flush at
+ // the earliest opportunity.
+ if (!rootDoesHavePassiveEffects) {
+ rootDoesHavePassiveEffects = true;
+ scheduleCallback(NormalPriority, () => {
+ flushPassiveEffects();
+ return null;
+ });
+ }
+ }
nextEffect = nextEffect.nextEffect;
}
}
@@ -1850,17 +1862,6 @@ function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
}
}
- if (effectTag & Passive) {
- // If there are passive effects, schedule a callback to flush them.
- if (!rootDoesHavePassiveEffects) {
- rootDoesHavePassiveEffects = true;
- scheduleCallback(NormalPriority, () => {
- flushPassiveEffects();
- return null;
- });
- }
- }
-
// The following switch statement is only concerned about placement,
// updates, and deletions. To avoid needing to add a case for every possible
// bitmap value, we remove the secondary effects from the effect tag and
diff --git a/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js
index 5fe90aac0e003..6f91e8323b2f9 100644
--- a/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js
@@ -281,13 +281,30 @@ describe('ReactSchedulerIntegration', () => {
});
return null;
}
+ function CleanupEffect() {
+ useLayoutEffect(() => () => {
+ Scheduler.unstable_yieldValue('Cleanup Layout Effect');
+ Scheduler.unstable_scheduleCallback(NormalPriority, () =>
+ Scheduler.unstable_yieldValue(
+ 'Scheduled Normal Callback from Cleanup Layout Effect',
+ ),
+ );
+ });
+ return null;
+ }
+ await ReactNoop.act(async () => {
+ ReactNoop.render();
+ });
+ expect(Scheduler).toHaveYielded([]);
await ReactNoop.act(async () => {
ReactNoop.render();
});
expect(Scheduler).toHaveYielded([
+ 'Cleanup Layout Effect',
'Layout Effect',
'Passive Effect',
- // This callback should be scheduled after the passive effects.
+ // These callbacks should be scheduled after the passive effects.
+ 'Scheduled Normal Callback from Cleanup Layout Effect',
'Scheduled Normal Callback from Layout Effect',
]);
});