diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index d055b271ad77..407cef2d176c 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -1114,11 +1114,29 @@ export function isUnsafeClassRenderPhaseUpdate(fiber: Fiber): boolean { return (executionContext & RenderContext) !== NoContext; } +// When execution is interrupted by a breakpoint, alert(), or a browser +// debugging pause, the finally blocks that reset executionContext may not run. +// When React resumes, executionContext still has RenderContext or CommitContext +// set even though we're not actually in the middle of work. Detect this by +// checking whether workInProgress/workInProgressRoot are null and clear the +// stale flags so we don't throw "Should not already be working." +function fixStaleExecutionContext(): void { + if ( + (executionContext & (RenderContext | CommitContext)) !== NoContext && + workInProgress === null && + workInProgressRoot === null + ) { + executionContext &= ~(RenderContext | CommitContext); + } +} + export function performWorkOnRoot( root: FiberRoot, lanes: Lanes, forceSync: boolean, ): void { + fixStaleExecutionContext(); + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { throw new Error('Should not already be working.'); } @@ -3511,6 +3529,8 @@ function completeRoot( } while (pendingEffectsStatus !== NO_PENDING_EFFECTS); flushRenderPhaseStrictModeWarningsInDEV(); + fixStaleExecutionContext(); + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { throw new Error('Should not already be working.'); }