(root: FiberRoot, thrownValue: any)
| 2209 | } |
| 2210 | |
| 2211 | function handleThrow(root: FiberRoot, thrownValue: any): void { |
| 2212 | // A component threw an exception. Usually this is because it suspended, but |
| 2213 | // it also includes regular program errors. |
| 2214 | // |
| 2215 | // We're either going to unwind the stack to show a Suspense or error |
| 2216 | // boundary, or we're going to replay the component again. Like after a |
| 2217 | // promise resolves. |
| 2218 | // |
| 2219 | // Until we decide whether we're going to unwind or replay, we should preserve |
| 2220 | // the current state of the work loop without resetting anything. |
| 2221 | // |
| 2222 | // If we do decide to unwind the stack, module-level variables will be reset |
| 2223 | // in resetSuspendedWorkLoopOnUnwind. |
| 2224 | |
| 2225 | // These should be reset immediately because they're only supposed to be set |
| 2226 | // when React is executing user code. |
| 2227 | resetHooksAfterThrow(); |
| 2228 | if (__DEV__) { |
| 2229 | resetCurrentFiber(); |
| 2230 | } |
| 2231 | |
| 2232 | if ( |
| 2233 | thrownValue === SuspenseException || |
| 2234 | thrownValue === SuspenseActionException |
| 2235 | ) { |
| 2236 | // This is a special type of exception used for Suspense. For historical |
| 2237 | // reasons, the rest of the Suspense implementation expects the thrown value |
| 2238 | // to be a thenable, because before `use` existed that was the (unstable) |
| 2239 | // API for suspending. This implementation detail can change later, once we |
| 2240 | // deprecate the old API in favor of `use`. |
| 2241 | thrownValue = getSuspendedThenable(); |
| 2242 | // TODO: Suspending the work loop during the render phase is |
| 2243 | // currently not compatible with sibling prerendering. We will add |
| 2244 | // this optimization back in a later step. |
| 2245 | // Don't suspend work loop, except to check if the data has |
| 2246 | // immediately resolved (i.e. in a microtask). Otherwise, trigger the |
| 2247 | // nearest Suspense fallback. |
| 2248 | workInProgressSuspendedReason = SuspendedOnImmediate; |
| 2249 | } else if (thrownValue === SuspenseyCommitException) { |
| 2250 | thrownValue = getSuspendedThenable(); |
| 2251 | workInProgressSuspendedReason = SuspendedOnInstance; |
| 2252 | } else if (thrownValue === SelectiveHydrationException) { |
| 2253 | // An update flowed into a dehydrated boundary. Before we can apply the |
| 2254 | // update, we need to finish hydrating. Interrupt the work-in-progress |
| 2255 | // render so we can restart at the hydration lane. |
| 2256 | // |
| 2257 | // The ideal implementation would be able to switch contexts without |
| 2258 | // unwinding the current stack. |
| 2259 | // |
| 2260 | // We could name this something more general but as of now it's the only |
| 2261 | // case where we think this should happen. |
| 2262 | workInProgressSuspendedReason = SuspendedOnHydration; |
| 2263 | } else { |
| 2264 | // This is a regular error. |
| 2265 | const isWakeable = |
| 2266 | thrownValue !== null && |
| 2267 | typeof thrownValue === 'object' && |
| 2268 | typeof thrownValue.then === 'function'; |
no test coverage detected