Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] improve Hoistable handling for Elements and Resources inside Suspense Boundaries #28069

Merged
merged 3 commits into from
Jan 30, 2024

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Jan 24, 2024

Updates Fizz to handle Hoistables (Resources and Elements) in a way that better aligns with Suspense fallbacks

  1. Hoistable Elements inside a fallback (regardless of how deep and how many additional boundaries are intermediate) will be ignored. The reasoning is fallbacks are transient and since there is not good way to clean up hoistables because they escape their Suspense container its better to not emit them in the first place. SSR fallbacks are already not full fidelity because they never hydrate so this aligns with that somewhat.
  2. Hoistable stylesheets in fallbacks will only block the reveal of a parent suspense boundary if the fallback is going to flush with that completed parent suspense boundary. Previously if you rendered a stylesheet Resource inside a fallback any parent suspense boundaries that completed after the shell flushed would include that resource in the set required to resolve before the boundary reveal happens on the client. This is not a semantic change, just a performance optimization
  3. preconnect and preload hoistable queues are gone, if you want to optimize resource loading you shoudl use ReactDOM.preconnect and ReactDOM.preload. viewport meta tags get their own queue because they need to go before any preloads since they affect the media state.

In addition to those functional changes this PR also refactors the boundary resource tracking by moving it to the task rather than using function calls at the start of each render and flush. Tasks also now track whether they are a fallback task

supercedes prior work here: #27534

Hoistable elements and resources in fallbacks should not flush unless the fallback itself flushes.

Previously we used a renderState-local binding to track the resources for the currently rendering boundary. Instead we now track this in the task itself and pass it to the functions that depend on it. This cleans up an unfortunate factoring that I put in before where during flushing we had to mimic the currently rendering Boundary to hoist correctly. This new factoring does the same thing but in a much clearer way.

I do track the Boundary state separately from the Boundary itself on the task as a hot path optimization to avoid having to existence check the boundary in `pushStartInstance`. Conceptually tracking the boundary itself is sufficient but this eliminates an extra condition check.

The implementation ended up not merging the boundary resource concept with hoistable state due to the very different nature in which these things need to hoist (one during task completion and the other during flush but only when flushing late boundaries). Given that I've gone back to resource specific naming rather than calling it BoundaryState.
@@ -5214,9 +5256,13 @@ type StylesheetResource = {
state: StylesheetState,
};

export type BoundaryResources = {
export type BoundaryState = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is the best name. I wanted something analagous to RenderState (akin to request state when combined with ResumableState) but since I can't really generalize this state b/c the Fizz runtime needs to understand the concept of hositables to a degree maybe I should name it HoistableState or something

Comment on lines 5263 to 5264
charsetChunks: Array<Chunk | PrecomputedChunk>,
viewportChunks: Array<Chunk | PrecomputedChunk>,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planning to move charset to config to avoid all this plumbing for something that should always be at the root and doesn't compose. Viewport is harder because it doesn't have to go at the top even though you'd definitely want it to.

@@ -222,6 +224,7 @@ type RenderTask = {
childIndex: number,
ping: () => void,
blockedBoundary: Root | SuspenseBoundary,
blockedBoundaryState: null | BoundaryState, // Boundary state we'll mutate while rendering. This may not equal the state of the blockedBoundary
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky. The blocked boundary is the one that can't actually be completed until all the tasks are done. For a fallback task this is the parent of the boundary the fallback is for. But for blockedBoundaryState it could the the state of the blockedBoundary (usually the case) but it could also be the fallbackState of the deeper boundary when the task is for generating the fallback. It's not really blocked so the terminology isn't quite right.

I think maybe renaming

  • BoundaryState to HoistableState and
  • blockedBounaryState to hoistableState
    Which may help with understanding

…ither during the preamble or when a boundary complets. The cost is that during the preamble flush there is an extra traversal of the segments that will write in the preamble. While this is not ideal for perf it does make the tracking significantly easier to follow and overall probalby strikes a better balance of practicality and maintainability over raw performance.
@gnoff gnoff force-pushed the complete-hoistables branch from 0db7029 to 852f521 Compare January 24, 2024 15:26
@gnoff
Copy link
Collaborator Author

gnoff commented Jan 24, 2024

I pushed an update which uses the HoistableState naming rathern than BoundaryState

I was also thinking about the ways we can still have hoistables leak into the DOM before a "commit" of a boundary.

I believe this can happen when

Root > Boundary A > Boundary B
imagine boundary A and Boundary B are partially complete. each flushes some segments but they aren't visible.
Then B completes. Upon it's completion it writes hoistables because it doesn't know about the state of it's parent. these end up in the DOM outside the hidden div of A

This is still much better than where we were before but it's unfortunate that it is the case

@react-sizebot
Copy link

react-sizebot commented Jan 24, 2024

Comparing: b2d6371...eed492e

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 176.24 kB 176.24 kB = 54.88 kB 54.88 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 178.34 kB 178.34 kB = 55.53 kB 55.53 kB
facebook-www/ReactDOM-prod.classic.js = 571.94 kB 571.94 kB = 100.56 kB 100.56 kB
facebook-www/ReactDOM-prod.modern.js = 555.70 kB 555.70 kB = 97.62 kB 97.62 kB
test_utils/ReactAllWarnings.js Deleted 67.55 kB 0.00 kB Deleted 16.54 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.js +0.48% 316.95 kB 318.49 kB +0.62% 68.93 kB 69.36 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.js +0.48% 316.98 kB 318.52 kB +0.62% 68.96 kB 69.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.js +0.48% 318.53 kB 320.07 kB +0.59% 69.63 kB 70.05 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.js +0.48% 318.56 kB 320.09 kB +0.59% 69.66 kB 70.07 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.js +0.48% 321.11 kB 322.65 kB +0.62% 69.70 kB 70.13 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.js +0.48% 321.14 kB 322.67 kB +0.61% 69.73 kB 70.15 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.js +0.48% 322.74 kB 324.27 kB +0.62% 70.98 kB 71.42 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.js +0.48% 322.76 kB 324.30 kB +0.61% 71.01 kB 71.44 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.js +0.47% 328.19 kB 329.73 kB +0.59% 70.93 kB 71.35 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.js +0.46% 338.72 kB 340.29 kB +0.56% 72.50 kB 72.91 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.js +0.46% 341.07 kB 342.64 kB +0.58% 72.94 kB 73.37 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.js +0.46% 343.24 kB 344.80 kB +0.57% 73.95 kB 74.37 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.45% 319.13 kB 320.56 kB +0.56% 69.49 kB 69.88 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.45% 319.15 kB 320.59 kB +0.55% 69.52 kB 69.91 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.js +0.44% 325.00 kB 326.43 kB +0.57% 71.28 kB 71.68 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.js +0.44% 325.02 kB 326.46 kB +0.57% 71.30 kB 71.71 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.43% 330.06 kB 331.49 kB +0.56% 71.38 kB 71.78 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.js +0.43% 336.23 kB 337.67 kB +0.54% 73.28 kB 73.68 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.38% 414.49 kB 416.08 kB +0.49% 92.78 kB 93.23 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.38% 414.52 kB 416.11 kB +0.48% 92.81 kB 93.26 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.38% 419.30 kB 420.89 kB +0.47% 93.86 kB 94.29 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.38% 419.33 kB 420.92 kB +0.47% 93.88 kB 94.32 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.38% 420.34 kB 421.93 kB +0.47% 94.92 kB 95.36 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.38% 420.36 kB 421.95 kB +0.47% 94.94 kB 95.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.38% 420.92 kB 422.51 kB +0.47% 95.05 kB 95.49 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.38% 420.95 kB 422.54 kB +0.47% 95.07 kB 95.52 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.37% 426.39 kB 427.98 kB +0.47% 94.92 kB 95.36 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.37% 439.97 kB 441.59 kB +0.46% 97.20 kB 97.65 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.37% 441.54 kB 443.16 kB +0.45% 97.98 kB 98.42 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.37% 442.13 kB 443.74 kB +0.45% 98.11 kB 98.55 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +0.36% 439.90 kB 441.49 kB +0.45% 95.93 kB 96.35 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +0.36% 439.92 kB 441.51 kB +0.44% 95.95 kB 96.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.35% 420.90 kB 422.39 kB +0.45% 94.76 kB 95.19 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.35% 420.93 kB 422.42 kB +0.45% 94.79 kB 95.21 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.35% 422.76 kB 424.24 kB +0.45% 95.23 kB 95.66 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.35% 422.78 kB 424.27 kB +0.45% 95.25 kB 95.68 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +0.35% 462.00 kB 463.62 kB +0.44% 99.03 kB 99.46 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.34% 432.80 kB 434.29 kB +0.44% 96.89 kB 97.32 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.34% 434.65 kB 436.14 kB +0.45% 97.36 kB 97.79 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +0.34% 440.48 kB 441.96 kB +0.43% 95.76 kB 96.17 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +0.34% 440.51 kB 441.99 kB +0.43% 95.79 kB 96.20 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +0.33% 452.87 kB 454.35 kB +0.42% 97.88 kB 98.29 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.31% 467.29 kB 468.75 kB +0.44% 95.10 kB 95.52 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.28% 6.34 kB 6.35 kB = 1.73 kB 1.72 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.28% 6.34 kB 6.35 kB = 1.73 kB 1.72 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.28% 6.34 kB 6.35 kB = 1.73 kB 1.72 kB
facebook-www/ReactDOMServer-dev.modern.js +0.28% 474.63 kB 475.98 kB +0.41% 96.74 kB 97.13 kB
facebook-www/ReactDOMServer-dev.classic.js +0.28% 483.08 kB 484.43 kB +0.41% 98.40 kB 98.80 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +0.25% 3.23 kB 3.24 kB = 1.21 kB 1.19 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +0.25% 3.23 kB 3.24 kB = 1.21 kB 1.19 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +0.25% 3.23 kB 3.24 kB = 1.21 kB 1.19 kB
oss-stable-semver/react-server/cjs/react-server.production.min.js +0.22% 38.26 kB 38.34 kB = 12.21 kB 12.19 kB
oss-stable/react-server/cjs/react-server.production.min.js +0.22% 38.26 kB 38.34 kB = 12.21 kB 12.19 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.min.js = 92.84 kB 92.55 kB = 28.41 kB 28.38 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js = 91.06 kB 90.77 kB = 27.61 kB 27.57 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js = 87.53 kB 87.24 kB = 26.99 kB 26.97 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js = 87.46 kB 87.18 kB = 26.69 kB 26.66 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.min.js = 86.26 kB 85.96 kB = 26.92 kB 26.85 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.min.js = 86.23 kB 85.93 kB = 26.89 kB 26.82 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js = 85.89 kB 85.59 kB = 25.94 kB 25.90 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js = 84.38 kB 84.08 kB = 26.06 kB 26.01 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js = 84.36 kB 84.05 kB = 26.04 kB 25.98 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js = 82.79 kB 82.49 kB = 25.06 kB 25.05 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js = 82.77 kB 82.47 kB = 25.04 kB 25.02 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js = 81.30 kB 81.01 kB = 25.60 kB 25.54 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js = 81.28 kB 80.98 kB = 25.57 kB 25.52 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js = 81.23 kB 80.93 kB = 25.24 kB 25.20 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js = 81.20 kB 80.90 kB = 25.22 kB 25.18 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js = 88.93 kB 88.59 kB +0.01% 27.07 kB 27.08 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js = 85.83 kB 85.50 kB = 26.30 kB 26.26 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js = 85.81 kB 85.48 kB = 26.27 kB 26.23 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js = 83.01 kB 82.69 kB = 25.15 kB 25.12 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js = 83.10 kB 82.76 kB = 25.53 kB 25.49 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js = 80.28 kB 79.96 kB = 24.38 kB 24.34 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js = 80.26 kB 79.93 kB = 24.36 kB 24.32 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js = 80.37 kB 80.04 kB = 24.79 kB 24.79 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js = 80.34 kB 80.01 kB = 24.77 kB 24.77 kB
test_utils/ReactAllWarnings.js Deleted 67.55 kB 0.00 kB Deleted 16.54 kB 0.00 kB

Generated by 🚫 dangerJS against eed492e

@sebmarkbage sebmarkbage self-requested a review January 24, 2024 17:39
@sebmarkbage
Copy link
Collaborator

As discussed, we'll do a write-as-we-go pass for the preamble, and stop writing hoistables inside fallbacks since we won't remove them. Fallbacks are not full fidelity anyway since they're not hydrated and it might not be worth the temporary state.

@gnoff gnoff force-pushed the complete-hoistables branch from 9a18ab4 to 0251ed2 Compare January 24, 2024 23:27
…able is not in a fallback tree. Effectively it makes hoistable elements deep in any fallback noops. The logic here is that fallbacks aren't hydrated, they're always either replaced by the server or client rendered. In either case the primary content should be replacing the fallback hositables and since hoistable elements are really just metadata the imperative need to transiently render them is not as well justified. This avoids a host of complexities around deleting hoistables from fallbacks when the boundary reveals it's content.

Along with this change I made it so primary content hositables flush eagerly and do not wait for their containing boundary to complete first. This is a progmatic solution to the problem of prerendering. when prerendering we assume that all transent state is entirely flushed in the static prelude however by holding back hoistables until the boundary is complete this is violated. We would either need to conditionally emit hoistables for incomplete boundaries when prerending or we would need to serialize the state of the hoistables to recover them on the resuem path. The arguments for holding them back are that it aligns with client hoistable semantics better and if the boundary never hydrates the hoistables will get orphaned. Both of these problems are not insignificant but are also not necessarily blockers and so this approach attempts to balance complexity over impact by emitting them eagerly.
@gnoff gnoff force-pushed the complete-hoistables branch from 0251ed2 to eed492e Compare January 25, 2024 04:30
@gnoff
Copy link
Collaborator Author

gnoff commented Jan 25, 2024

@sebmarkbage updated description with slightly modified approach. basically what we discussed but now primary content hoistables can and will flush early to avoid the problem of lost state across prerender/resume

@gnoff gnoff changed the title [Fizz] only emit Hoistable Elements that are part of a completed boundary [Fizz] only emit Hoistable Elements from primary content of Suspense boundary Jan 25, 2024
@sebmarkbage
Copy link
Collaborator

Maybe update the description to include the rationale for why leaving things emitting early is conceptually ok.

We decided that emitting hoistables before the Suspense boundary is revealed because we also emit hidden segments early. This means that a naive parser would find things early anyway. It also doesn't really hurt revealing it early because the idea is that it'll eventually end up showing up anyway.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the isFallback flag. Seems unnecessary brute force and specific.

It felt a little cleaner to just always write the hoistables to the boundary's fallback hoistables and then just not hoist/flush them but not sure if that strategy is incompatible with your other change.

@gnoff
Copy link
Collaborator Author

gnoff commented Jan 26, 2024

It felt a little cleaner to just always write the hoistables to the boundary's fallback hoistables and then just not hoist/flush them but not sure if that strategy is incompatible with your other change.

Conceptually cleaner but wasteful compute. we're never going to flush so why bother encoding. I don't love the flag either but would rather not do extra work for no gain

@lubieowoce
Copy link
Contributor

lubieowoce commented Jan 26, 2024

Sorry to butt in, but does this affect inline styles? i think those are considered hoistable too, right?

<style data-href={hash} precedence=“whatever”>
 {content}
</style>

(as in: https://x.com/sebmarkbage/status/1748900031199879298?s=20)

i think this is something CSS-in-JS (and adjacent) libraries are counting on, so not being able to use it for styling within fallbacks would be unfortunate...

@gnoff
Copy link
Collaborator Author

gnoff commented Jan 30, 2024

@lubieowoce there are two kinds of Hoistable semantics

Hoistable Elements are just regular html tags that happen to flush (SSR) or insert (browser) outside of the component tree that rendered them. This is the nature of what makes cleaning them up on boundary completion hard because before this semantic existed everything inside a Suspense was rendered structurally as a child of that boundary in the DOM.

This PR makes it so Hoistable Elements that are SSR'd in a fallback simply never flush. In practice this is the title tag, meta tags, and link tags that are not Hoistable Resources (stylesheets with precedence)

Hoistable Resources are a more distinct concept. Most Resources are created using ReactDOM.preload/preinit/.... functions but there are some that can be rendered such as <link rel="stylesheet" href="..." precedence="..." />. When you render a Resource you are marking that the component tree at this point depends on this external Resource. You can render them many times in your App but the Resource will only exist in the DOM once and it will be inserted/flushed in the head or body (depending on Resource type and timing). In this case the Component tree doesn't own the Resource it simply expresses a dependency on it. So Resources in fallbacks are ok because you can use them and then later when you stop depending on the Resource it doesn't semantically require cleanup.

The same holds for style tags that have href and precedence. this is an analog to a Stylesheet Resource but just for inline styles so you can depend on the Resource in a fallback and it will be flushed before the fallback is.

It's important to note though that Resources have no cleanup guarantees. Today Resources are never cleaned up and in the future we will add some form of garbage collection but it isn't a discrete process and any given Resource could stay around in the DOM for arbitrary time.

So to directly answer the question, this PR won't affect the style tag example you gave and that is because Hoistable Elements are distinct from Hoistable Resources.

@gnoff gnoff changed the title [Fizz] only emit Hoistable Elements from primary content of Suspense boundary [Fizz] improve Hoistable handling for Elements and Resources inside Suspense Boundaries Jan 30, 2024
@gnoff gnoff merged commit 554fc49 into facebook:main Jan 30, 2024
36 checks passed
@gnoff gnoff deleted the complete-hoistables branch January 30, 2024 18:15
github-actions bot pushed a commit that referenced this pull request Jan 30, 2024
…uspense Boundaries (#28069)

Updates Fizz to handle Hoistables (Resources and Elements) in a way that
better aligns with Suspense fallbacks

1. Hoistable Elements inside a fallback (regardless of how deep and how
many additional boundaries are intermediate) will be ignored. The
reasoning is fallbacks are transient and since there is not good way to
clean up hoistables because they escape their Suspense container its
better to not emit them in the first place. SSR fallbacks are already
not full fidelity because they never hydrate so this aligns with that
somewhat.
2. Hoistable stylesheets in fallbacks will only block the reveal of a
parent suspense boundary if the fallback is going to flush with that
completed parent suspense boundary. Previously if you rendered a
stylesheet Resource inside a fallback any parent suspense boundaries
that completed after the shell flushed would include that resource in
the set required to resolve before the boundary reveal happens on the
client. This is not a semantic change, just a performance optimization
3. preconnect and preload hoistable queues are gone, if you want to
optimize resource loading you shoudl use `ReactDOM.preconnect` and
`ReactDOM.preload`. `viewport` meta tags get their own queue because
they need to go before any preloads since they affect the media state.

In addition to those functional changes this PR also refactors the
boundary resource tracking by moving it to the task rather than using
function calls at the start of each render and flush. Tasks also now
track whether they are a fallback task

supercedes prior work here: #27534

DiffTrain build for [554fc49](554fc49)
@lubieowoce
Copy link
Contributor

lubieowoce commented Jan 30, 2024

@gnoff that's a lovely explanation, thank you!

gnoff added a commit to gnoff/next.js that referenced this pull request Feb 1, 2024
- facebook/react#28183
- facebook/react#28125
- facebook/react#28157
- facebook/react#28115
- facebook/react#28124
- facebook/react#28163
- facebook/react#28164
- facebook/react#28150
- facebook/react#28159
- facebook/react#28069
- facebook/react#28110
- facebook/react#28148
- facebook/react#28116
- facebook/react#28099
- facebook/react#28100
- facebook/react#28147
- facebook/react#28128
- facebook/react#28126
- facebook/react#28139
- facebook/react#28140
- facebook/react#28141
- facebook/react#28142
- facebook/react#28113
- facebook/react#28129
- facebook/react#28114
- facebook/react#28053
- facebook/react#28091
- facebook/react#28087
- facebook/react#28112
- facebook/react#28086
- facebook/react#28101
- facebook/react#28106
- facebook/react#28117
- facebook/react#28118
- facebook/react#28105
- facebook/react#27883
- facebook/react#28111
- facebook/react#28095
- facebook/react#28108
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
gaearon pushed a commit that referenced this pull request Feb 3, 2024
…uspense Boundaries (#28069)

Updates Fizz to handle Hoistables (Resources and Elements) in a way that
better aligns with Suspense fallbacks

1. Hoistable Elements inside a fallback (regardless of how deep and how
many additional boundaries are intermediate) will be ignored. The
reasoning is fallbacks are transient and since there is not good way to
clean up hoistables because they escape their Suspense container its
better to not emit them in the first place. SSR fallbacks are already
not full fidelity because they never hydrate so this aligns with that
somewhat.
2. Hoistable stylesheets in fallbacks will only block the reveal of a
parent suspense boundary if the fallback is going to flush with that
completed parent suspense boundary. Previously if you rendered a
stylesheet Resource inside a fallback any parent suspense boundaries
that completed after the shell flushed would include that resource in
the set required to resolve before the boundary reveal happens on the
client. This is not a semantic change, just a performance optimization
3. preconnect and preload hoistable queues are gone, if you want to
optimize resource loading you shoudl use `ReactDOM.preconnect` and
`ReactDOM.preload`. `viewport` meta tags get their own queue because
they need to go before any preloads since they affect the media state.

In addition to those functional changes this PR also refactors the
boundary resource tracking by moving it to the task rather than using
function calls at the start of each render and flush. Tasks also now
track whether they are a fallback task

supercedes prior work here: #27534
gnoff added a commit to gnoff/next.js that referenced this pull request Feb 5, 2024
- facebook/react#28183
- facebook/react#28125
- facebook/react#28157
- facebook/react#28115
- facebook/react#28124
- facebook/react#28163
- facebook/react#28164
- facebook/react#28150
- facebook/react#28159
- facebook/react#28069
- facebook/react#28110
- facebook/react#28148
- facebook/react#28116
- facebook/react#28099
- facebook/react#28100
- facebook/react#28147
- facebook/react#28128
- facebook/react#28126
- facebook/react#28139
- facebook/react#28140
- facebook/react#28141
- facebook/react#28142
- facebook/react#28113
- facebook/react#28129
- facebook/react#28114
- facebook/react#28053
- facebook/react#28091
- facebook/react#28087
- facebook/react#28112
- facebook/react#28086
- facebook/react#28101
- facebook/react#28106
- facebook/react#28117
- facebook/react#28118
- facebook/react#28105
- facebook/react#27883
- facebook/react#28111
- facebook/react#28095
- facebook/react#28108
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
gnoff added a commit to gnoff/next.js that referenced this pull request Feb 6, 2024
- facebook/react#28250
- facebook/react#28225
- facebook/react#28123
- facebook/react#28240
- facebook/react#28239
- facebook/react#28245
- facebook/react#28244
- facebook/react#28238
- facebook/react#28235
- facebook/react#28221
- facebook/react#28215
- facebook/react#28214
- facebook/react#28213
- facebook/react#28212
- facebook/react#28211
- facebook/react#28247
- facebook/react#28210
- facebook/react#28186
- facebook/react#28232
- facebook/react#28169
- facebook/react#28177
- facebook/react#28170
- facebook/react#28168
- facebook/react#28122
- facebook/react#27982
- facebook/react#28217
- facebook/react#28223
- facebook/react#28208
- facebook/react#28209
- facebook/react#28200
- facebook/react#28199
- facebook/react#28198
- facebook/react#28197
- facebook/react#28196
- facebook/react#28194
- facebook/react#28192
- facebook/react#28191
- facebook/react#28182
- facebook/react#28181
- facebook/react#28180
- facebook/react#28178
- facebook/react#28201
- facebook/react#28176
- facebook/react#28162
- facebook/react#28131
- facebook/react#28190
- facebook/react#28172
- facebook/react#28171
- facebook/react#28173
- facebook/react#28174
- facebook/react#28175
- facebook/react#28136
- facebook/react#28135
- facebook/react#28134
- facebook/react#28133
- facebook/react#28132
- facebook/react#28130
- facebook/react#28202
- facebook/react#28102
- facebook/react#28161
- facebook/react#28193
- facebook/react#28195
- facebook/react#28189
- facebook/react#28160
- facebook/react#28096
- facebook/react#28183
- facebook/react#28125
- facebook/react#28157
- facebook/react#28115
- facebook/react#28124
- facebook/react#28163
- facebook/react#28164
- facebook/react#28150
- facebook/react#28159
- facebook/react#28069
- facebook/react#28110
- facebook/react#28148
- facebook/react#28116
- facebook/react#28099
- facebook/react#28100
- facebook/react#28147
- facebook/react#28128
- facebook/react#28126
- facebook/react#28139
- facebook/react#28140
- facebook/react#28141
- facebook/react#28142
- facebook/react#28113
- facebook/react#28129
- facebook/react#28114
- facebook/react#28053
- facebook/react#28091
- facebook/react#28087
- facebook/react#28112
- facebook/react#28086
- facebook/react#28101
- facebook/react#28106
- facebook/react#28117
- facebook/react#28118
- facebook/react#28105
- facebook/react#27883
- facebook/react#28111
- facebook/react#28095
- facebook/react#28108
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
gnoff added a commit to gnoff/next.js that referenced this pull request Feb 6, 2024
- facebook/react#28250
- facebook/react#28225
- facebook/react#28123
- facebook/react#28240
- facebook/react#28239
- facebook/react#28245
- facebook/react#28244
- facebook/react#28238
- facebook/react#28235
- facebook/react#28221
- facebook/react#28215
- facebook/react#28214
- facebook/react#28213
- facebook/react#28212
- facebook/react#28211
- facebook/react#28247
- facebook/react#28210
- facebook/react#28186
- facebook/react#28232
- facebook/react#28169
- facebook/react#28177
- facebook/react#28170
- facebook/react#28168
- facebook/react#28122
- facebook/react#27982
- facebook/react#28217
- facebook/react#28223
- facebook/react#28208
- facebook/react#28209
- facebook/react#28200
- facebook/react#28199
- facebook/react#28198
- facebook/react#28197
- facebook/react#28196
- facebook/react#28194
- facebook/react#28192
- facebook/react#28191
- facebook/react#28182
- facebook/react#28181
- facebook/react#28180
- facebook/react#28178
- facebook/react#28201
- facebook/react#28176
- facebook/react#28162
- facebook/react#28131
- facebook/react#28190
- facebook/react#28172
- facebook/react#28171
- facebook/react#28173
- facebook/react#28174
- facebook/react#28175
- facebook/react#28136
- facebook/react#28135
- facebook/react#28134
- facebook/react#28133
- facebook/react#28132
- facebook/react#28130
- facebook/react#28202
- facebook/react#28102
- facebook/react#28161
- facebook/react#28193
- facebook/react#28195
- facebook/react#28189
- facebook/react#28160
- facebook/react#28096
- facebook/react#28183
- facebook/react#28125
- facebook/react#28157
- facebook/react#28115
- facebook/react#28124
- facebook/react#28163
- facebook/react#28164
- facebook/react#28150
- facebook/react#28159
- facebook/react#28069
- facebook/react#28110
- facebook/react#28148
- facebook/react#28116
- facebook/react#28099
- facebook/react#28100
- facebook/react#28147
- facebook/react#28128
- facebook/react#28126
- facebook/react#28139
- facebook/react#28140
- facebook/react#28141
- facebook/react#28142
- facebook/react#28113
- facebook/react#28129
- facebook/react#28114
- facebook/react#28053
- facebook/react#28091
- facebook/react#28087
- facebook/react#28112
- facebook/react#28086
- facebook/react#28101
- facebook/react#28106
- facebook/react#28117
- facebook/react#28118
- facebook/react#28105
- facebook/react#27883
- facebook/react#28111
- facebook/react#28095
- facebook/react#28108
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
gnoff added a commit to vercel/next.js that referenced this pull request Feb 6, 2024
Updates React from 60a927d04 to 2bc7d336a

Also updates aliases for `react.shared-subset` to `react.react-server`

### React upstream changes

- facebook/react#28250
- facebook/react#28225
- facebook/react#28123
- facebook/react#28240
- facebook/react#28239
- facebook/react#28245
- facebook/react#28244
- facebook/react#28238
- facebook/react#28235
- facebook/react#28221
- facebook/react#28215
- facebook/react#28214
- facebook/react#28213
- facebook/react#28212
- facebook/react#28211
- facebook/react#28247
- facebook/react#28210
- facebook/react#28186
- facebook/react#28232
- facebook/react#28169
- facebook/react#28177
- facebook/react#28170
- facebook/react#28168
- facebook/react#28122
- facebook/react#27982
- facebook/react#28217
- facebook/react#28223
- facebook/react#28208
- facebook/react#28209
- facebook/react#28200
- facebook/react#28199
- facebook/react#28198
- facebook/react#28197
- facebook/react#28196
- facebook/react#28194
- facebook/react#28192
- facebook/react#28191
- facebook/react#28182
- facebook/react#28181
- facebook/react#28180
- facebook/react#28178
- facebook/react#28201
- facebook/react#28176
- facebook/react#28162
- facebook/react#28131
- facebook/react#28190
- facebook/react#28172
- facebook/react#28171
- facebook/react#28173
- facebook/react#28174
- facebook/react#28175
- facebook/react#28136
- facebook/react#28135
- facebook/react#28134
- facebook/react#28133
- facebook/react#28132
- facebook/react#28130
- facebook/react#28202
- facebook/react#28102
- facebook/react#28161
- facebook/react#28193
- facebook/react#28195
- facebook/react#28189
- facebook/react#28160
- facebook/react#28096
- facebook/react#28183
- facebook/react#28125
- facebook/react#28157
- facebook/react#28115
- facebook/react#28124
- facebook/react#28163
- facebook/react#28164
- facebook/react#28150
- facebook/react#28159
- facebook/react#28069
- facebook/react#28110
- facebook/react#28148
- facebook/react#28116
- facebook/react#28099
- facebook/react#28100
- facebook/react#28147
- facebook/react#28128
- facebook/react#28126
- facebook/react#28139
- facebook/react#28140
- facebook/react#28141
- facebook/react#28142
- facebook/react#28113
- facebook/react#28129
- facebook/react#28114
- facebook/react#28053
- facebook/react#28091
- facebook/react#28087
- facebook/react#28112
- facebook/react#28086
- facebook/react#28101
- facebook/react#28106
- facebook/react#28117
- facebook/react#28118
- facebook/react#28105
- facebook/react#27883
- facebook/react#28111
- facebook/react#28095
- facebook/react#28108
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939
- facebook/react#28090
- facebook/react#28089
- facebook/react#28076
- facebook/react#28074
- facebook/react#28103
- facebook/react#28098
- facebook/react#28097
- facebook/react#28068
- facebook/react#28093
- facebook/react#28094
- facebook/react#28073
- facebook/react#28084
- facebook/react#28063
- facebook/react#28085
- facebook/react#28083
- facebook/react#28065
- facebook/react#28061
- facebook/react#28077
- facebook/react#28075
- facebook/react#28078
- facebook/react#28050
- facebook/react#28011
- facebook/react#28055
- facebook/react#28066
- facebook/react#28067
- facebook/react#28010
- facebook/react#27993
- facebook/react#28052
- facebook/react#28060
- facebook/react#28059
- facebook/react#28034
- facebook/react#28033
- facebook/react#28004
- facebook/react#28051
- facebook/react#28012
- facebook/react#28001
- facebook/react#28002
- facebook/react#27995
- facebook/react#28006
- facebook/react#28005
- facebook/react#28007
- facebook/react#28008
- facebook/react#28009
- facebook/react#28000
- facebook/react#28003
- facebook/react#27997
- facebook/react#27240
- facebook/react#27977
- facebook/react#27940
- facebook/react#27939

Closes NEXT-2331
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…uspense Boundaries (facebook#28069)

Updates Fizz to handle Hoistables (Resources and Elements) in a way that
better aligns with Suspense fallbacks

1. Hoistable Elements inside a fallback (regardless of how deep and how
many additional boundaries are intermediate) will be ignored. The
reasoning is fallbacks are transient and since there is not good way to
clean up hoistables because they escape their Suspense container its
better to not emit them in the first place. SSR fallbacks are already
not full fidelity because they never hydrate so this aligns with that
somewhat.
2. Hoistable stylesheets in fallbacks will only block the reveal of a
parent suspense boundary if the fallback is going to flush with that
completed parent suspense boundary. Previously if you rendered a
stylesheet Resource inside a fallback any parent suspense boundaries
that completed after the shell flushed would include that resource in
the set required to resolve before the boundary reveal happens on the
client. This is not a semantic change, just a performance optimization
3. preconnect and preload hoistable queues are gone, if you want to
optimize resource loading you shoudl use `ReactDOM.preconnect` and
`ReactDOM.preload`. `viewport` meta tags get their own queue because
they need to go before any preloads since they affect the media state.

In addition to those functional changes this PR also refactors the
boundary resource tracking by moving it to the task rather than using
function calls at the start of each render and flush. Tasks also now
track whether they are a fallback task

supercedes prior work here: facebook#27534
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…uspense Boundaries (#28069)

Updates Fizz to handle Hoistables (Resources and Elements) in a way that
better aligns with Suspense fallbacks

1. Hoistable Elements inside a fallback (regardless of how deep and how
many additional boundaries are intermediate) will be ignored. The
reasoning is fallbacks are transient and since there is not good way to
clean up hoistables because they escape their Suspense container its
better to not emit them in the first place. SSR fallbacks are already
not full fidelity because they never hydrate so this aligns with that
somewhat.
2. Hoistable stylesheets in fallbacks will only block the reveal of a
parent suspense boundary if the fallback is going to flush with that
completed parent suspense boundary. Previously if you rendered a
stylesheet Resource inside a fallback any parent suspense boundaries
that completed after the shell flushed would include that resource in
the set required to resolve before the boundary reveal happens on the
client. This is not a semantic change, just a performance optimization
3. preconnect and preload hoistable queues are gone, if you want to
optimize resource loading you shoudl use `ReactDOM.preconnect` and
`ReactDOM.preload`. `viewport` meta tags get their own queue because
they need to go before any preloads since they affect the media state.

In addition to those functional changes this PR also refactors the
boundary resource tracking by moving it to the task rather than using
function calls at the start of each render and flush. Tasks also now
track whether they are a fallback task

supercedes prior work here: #27534

DiffTrain build for commit 554fc49.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants