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', ]); });