From d20f72cc728d24320cd7a18c0c4834478f9493db Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 1 Mar 2023 11:15:18 -0500 Subject: [PATCH] Fix: Incorrect type of thenable passed to DevTools Selective hydration is implemented by suspending the current render using a special internal opaque object. This is conceptually similar to suspending with a thenable in userspace, but the opaque object should not leak outside of the reconciler. We were accidentally passing this object to DevTool's markComponentSuspended function, which expects an actual thenable. This happens in the error handling path (handleThrow). The fix is to check for the exception reason before calling markComponentSuspended. There was already a naive check in place, but it didn't account for all possible enum values of the exception reason. --- .../src/ReactFiberWorkLoop.js | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 6d29eb0212f26..fd6e51a0872bc 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -1846,19 +1846,34 @@ function handleThrow(root: FiberRoot, thrownValue: any): void { if (enableSchedulingProfiler) { markComponentRenderStopped(); - if (workInProgressSuspendedReason !== SuspendedOnError) { - const wakeable: Wakeable = (thrownValue: any); - markComponentSuspended( - erroredWork, - wakeable, - workInProgressRootRenderLanes, - ); - } else { - markComponentErrored( - erroredWork, - thrownValue, - workInProgressRootRenderLanes, - ); + switch (workInProgressSuspendedReason) { + case SuspendedOnError: { + markComponentErrored( + erroredWork, + thrownValue, + workInProgressRootRenderLanes, + ); + break; + } + case SuspendedOnData: + case SuspendedOnImmediate: + case SuspendedOnDeprecatedThrowPromise: + case SuspendedAndReadyToUnwind: { + const wakeable: Wakeable = (thrownValue: any); + markComponentSuspended( + erroredWork, + wakeable, + workInProgressRootRenderLanes, + ); + break; + } + case SuspendedOnHydration: { + // This is conceptually like a suspend, but it's not associated with + // a particular wakeable. DevTools doesn't seem to care about this case, + // currently. It's similar to if the component were interrupted, which + // we don't mark with a special function. + break; + } } } }