[pull] main from react:main - #621
Merged
Merged
Conversation
…tree (#37135) Closes vercel/next.js#95848. Fixes a hang: if an update changes what's inside a server-rendered Suspense or Activity boundary before that boundary has hydrated, and the affected content is hidden, React stops committing. The new content renders once its data arrives, but the render is discarded every time, nothing is scheduled, and nothing ever pings — the update never lands and the app appears frozen. "Hidden" means either of two things, and there's a test for each: - the update itself hides a dehydrated `<Activity>` (while mounting new sibling content that suspends), or - the dehydrated boundary is inside the primary tree of a parent boundary that just suspended and is showing its fallback. This is how we found it in practice. With vercel/next.js#95682, pressing Back before hydration finishes made the router replay the missed navigation from its first effect. This worked fine outside of Cache Components, but in Cache Components mode (which turns on Activity), the old page's Activity (still dehydrated) gets hidden, the new page's content suspends inside the layout's Suspense boundary, and after the data arrives the page stays blank forever. As a result, vercel/next.js#95682 got reverted. If we fix this, we can unrevert it. ## Why it happens When an update changes a dehydrated boundary, we schedule a render at a higher priority to hydrate it before the update applies. If we already tried that, we give up and client render, but mark the render as suspended so it doesn't commit while the hydration attempt might still finish first. Both steps assume the attempt can actually run. Inside a hidden tree it can't, because updates in hidden trees are deferred until the tree is revealed. The scheduled attempt never runs but still consumes the retry lane, which sends every later render into the give-up path — and the give-up path keeps discarding finished renders, waiting for a hydration attempt that isn't in flight. Once the last piece of data resolves there's nothing left to ping us awake. The root ends up with `pendingLanes === suspendedLanes`, `pingedLanes` empty, and no callback scheduled. The update doesn't need to be sync or discrete: a plain setState from an effect is enough. Wrapping the same update in `startTransition` avoids it, which is probably why this went unnoticed. ## The fix If the boundary is inside a hidden tree (`isCurrentTreeHidden()`), skip the hydration attempt and client render right away. There's nothing visible to protect: replacing hidden server HTML doesn't show, and the replacement children render when the tree is revealed. One behavior note: this discards the hidden server HTML instead of preserving it for later hydration on reveal, same as the existing give-up path. Keeping it dehydrated and hydrating at reveal would be a nicer follow-up, but needs commit-phase support that doesn't exist today. ## How did you test this change? The first commit adds failing tests for both boundary types; the fix makes them pass. `startTransition` variants of the same scenarios are included as passing controls. Ran the Activity, partial/selective hydration, Fizz, Suspense, and Offscreen suites in both release channels.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )