( current: null | Fiber, workInProgress: Fiber, renderLanes: Lanes, )
| 2340 | } |
| 2341 | |
| 2342 | function updateSuspenseComponent( |
| 2343 | current: null | Fiber, |
| 2344 | workInProgress: Fiber, |
| 2345 | renderLanes: Lanes, |
| 2346 | ) { |
| 2347 | const nextProps: SuspenseProps = workInProgress.pendingProps; |
| 2348 | |
| 2349 | // This is used by DevTools to force a boundary to suspend. |
| 2350 | if (__DEV__) { |
| 2351 | if (shouldSuspend(workInProgress)) { |
| 2352 | workInProgress.flags |= DidCapture; |
| 2353 | } |
| 2354 | } |
| 2355 | |
| 2356 | let showFallback = false; |
| 2357 | const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; |
| 2358 | if ( |
| 2359 | didSuspend || |
| 2360 | shouldRemainOnFallback(current, workInProgress, renderLanes) |
| 2361 | ) { |
| 2362 | // Something in this boundary's subtree already suspended. Switch to |
| 2363 | // rendering the fallback children. |
| 2364 | showFallback = true; |
| 2365 | workInProgress.flags &= ~DidCapture; |
| 2366 | } |
| 2367 | |
| 2368 | // Check if the primary children spawned a deferred task (useDeferredValue) |
| 2369 | // during the first pass. |
| 2370 | const didPrimaryChildrenDefer = (workInProgress.flags & DidDefer) !== NoFlags; |
| 2371 | workInProgress.flags &= ~DidDefer; |
| 2372 | |
| 2373 | // OK, the next part is confusing. We're about to reconcile the Suspense |
| 2374 | // boundary's children. This involves some custom reconciliation logic. Two |
| 2375 | // main reasons this is so complicated. |
| 2376 | // |
| 2377 | // First, Legacy Mode has different semantics for backwards compatibility. The |
| 2378 | // primary tree will commit in an inconsistent state, so when we do the |
| 2379 | // second pass to render the fallback, we do some exceedingly, uh, clever |
| 2380 | // hacks to make that not totally break. Like transferring effects and |
| 2381 | // deletions from hidden tree. In Concurrent Mode, it's much simpler, |
| 2382 | // because we bailout on the primary tree completely and leave it in its old |
| 2383 | // state, no effects. Same as what we do for Offscreen (except that |
| 2384 | // Offscreen doesn't have the first render pass). |
| 2385 | // |
| 2386 | // Second is hydration. During hydration, the Suspense fiber has a slightly |
| 2387 | // different layout, where the child points to a dehydrated fragment, which |
| 2388 | // contains the DOM rendered by the server. |
| 2389 | // |
| 2390 | // Third, even if you set all that aside, Suspense is like error boundaries in |
| 2391 | // that we first we try to render one tree, and if that fails, we render again |
| 2392 | // and switch to a different tree. Like a try/catch block. So we have to track |
| 2393 | // which branch we're currently rendering. Ideally we would model this using |
| 2394 | // a stack. |
| 2395 | if (current === null) { |
| 2396 | // Initial mount |
| 2397 | |
| 2398 | // Special path for hydration |
| 2399 | // If we're currently hydrating, try to hydrate this boundary. |
no test coverage detected