diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index a71eb543cfe5d..28a8c733d105a 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -3278,6 +3278,9 @@ function commitRootImpl( // We don't schedule a separate task for flushing passive effects. // Instead, we just rely on ensureRootIsScheduled below to schedule // a callback for us to flush the passive effects. + // We do need to clear this callback. + // Otherwise, we'll still think the commit is suspended. + root.cancelPendingCommit = null; } else { // So we can clear these now to allow a new callback to be scheduled. root.callbackNode = null; diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js index 4dbba1bca21f0..d946a473d67a0 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js @@ -491,4 +491,44 @@ describe('ReactSuspenseyCommitPhase', () => { , ); }); + + it('runs passive effects after suspended commit resolves', async () => { + function Effect() { + React.useEffect(() => { + Scheduler.log('flush effect'); + }); + return ; + } + + const root = ReactNoop.createRoot(); + + await act(() => { + root.render( + }> + + + , + ); + }); + + assertLog([ + 'render effect', + 'Image requested [A]', + 'Loading...', + 'render effect', + ]); + expect(root).toMatchRenderedOutput('Loading...'); + + await act(() => { + resolveSuspenseyThing('A'); + }); + + assertLog(['flush effect']); + expect(root).toMatchRenderedOutput( + <> + {'render effect'} + + , + ); + }); });