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
Add SuspenseList Component #15902
Add SuspenseList Component #15902
Conversation
|
ReactDOM: size: 0.0%, gzip: -0.0% Details of bundled changes.Comparing: cd98e63...d79e225 react-dom
react-art
react-test-renderer
react-native-renderer
react-reconciler
Generated by 🚫 dangerJS |
ae8e64a
to
49e6e1a
Compare
In the "together" mode, we do a second render pass that forces the fallbacks to stay in place, if not all can unsuspend at once.
This way, we end up retrying the SuspenseList in case the nested boundary that just suspended doesn't actually get mounted with this set of thennables. This happens when the second pass renders the fallback directly without first attempting to render the content.
Display order has some "display list" connotations making it sound like a z-index thing. Reveal indicates that this isn't really about when something gets rendered or is ready to be rendered. It's about when content that is already there gets to be revealed.
c11553d
to
431cffe
Compare
|
I realized that this probably doesn't work in legacy mode. I'll probably just disable it somehow. EDIT: I made it a noop similar to how other Suspense features behave in legacy mode. That way you can have code that works in concurrent/batched mode, kind of work in legacy mode. |
This will be used for more things in the directional case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left review comments in #15918
* Add SuspenseList component type * Push SuspenseContext for SuspenseList * Force Suspense boundaries into their fallback state In the "together" mode, we do a second render pass that forces the fallbacks to stay in place, if not all can unsuspend at once. * Add test * Transfer thennables to the SuspenseList This way, we end up retrying the SuspenseList in case the nested boundary that just suspended doesn't actually get mounted with this set of thennables. This happens when the second pass renders the fallback directly without first attempting to render the content. * Add warning for unsupported displayOrder * Add tests for nested sibling boundaries and nested lists * Fix nested SuspenseList forwarding thennables * Rename displayOrder to revealOrder Display order has some "display list" connotations making it sound like a z-index thing. Reveal indicates that this isn't really about when something gets rendered or is ready to be rendered. It's about when content that is already there gets to be revealed. * Add test for avoided boundaries * Make SuspenseList a noop in legacy mode * Use an explicit suspense list state object This will be used for more things in the directional case.
This component lets you coordinate the loading sequence of nested Suspense boundaries. How this happens is defined by the
revealOrderproperty. By default, it behaves like a noop so the nested boundaries just do what they normally would.It operates on the nearest most Suspense boundary but you can actually have intermediate components and DOM nodes. If the nearest nested boundary is another SuspenseList, it operates on the nested Suspense boundary within that.
This first PR implements the
"together"option which ensures that all boundaries resolve together. If any of them trigger a fallback, they all do.Subsequent PRs will implement ordered rendering row-by-row. However, even in those options when there are insertions in the middle or existing rows triggering new fallbacks in the middle, there will be a "together" pass to resolve those first before the row-by-row kicks in. So the
"together"mode is the primitive needed for those modes too.The mechanism for this mode is that we do a full render pass first, and then scan the result to see if there are any fallbacks. If there are, we do a full second render pass (including re-reconciliation). There are many complex scenarios that can happen such as having existing trees staying in fallbacks, and new props coming in.
The second pass currently deletes all work that we did in the first pass so it's quite in efficient but this particular mode is expected to be rare and not have large subtrees. It's expected that there are not that many abstractions until we hit the first Suspense boundary. We're Suspended anyway.
Unfortunately this does a shallow scan even if we're just updating something nested inside the tree. I'd like to add an optimization if nothing has suspended during the first render pass and we know from previous results that nothing has suspended. Not quite sure exactly when that's safe yet.