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

Add SuspenseList Component #15902

Merged
merged 12 commits into from Jun 20, 2019
Merged

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Jun 17, 2019

This component lets you coordinate the loading sequence of nested Suspense boundaries. How this happens is defined by the revealOrder property. 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.

@sizebot
Copy link

sizebot commented Jun 17, 2019

ReactDOM: size: 0.0%, gzip: -0.0%

Details of bundled changes.

Comparing: cd98e63...d79e225

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.profiling.min.js +1.6% +1.5% 109.44 KB 111.22 KB 34.78 KB 35.31 KB NODE_PROFILING
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 60.76 KB 60.76 KB 15.85 KB 15.85 KB UMD_DEV
ReactDOM-dev.js +1.3% +1.1% 889.04 KB 900.35 KB 197.96 KB 200.22 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.0% 10.68 KB 10.68 KB 3.67 KB 3.67 KB UMD_PROD
ReactDOMServer-dev.js +0.1% +0.1% 135.46 KB 135.64 KB 34.8 KB 34.84 KB FB_WWW_DEV
react-dom-unstable-fire.development.js +1.3% +1.1% 869.29 KB 880.33 KB 198.09 KB 200.33 KB UMD_DEV
react-dom-unstable-fire.production.min.js 🔺+1.7% 🔺+1.2% 106.03 KB 107.8 KB 34.49 KB 34.92 KB UMD_PROD
react-dom-unstable-fire.profiling.min.js +1.6% +1.2% 109.26 KB 111.02 KB 35.52 KB 35.95 KB UMD_PROFILING
react-dom-unstable-fire.development.js +1.3% +1.1% 863.65 KB 874.68 KB 196.52 KB 198.75 KB NODE_DEV
react-dom-server.node.development.js +0.1% +0.1% 135.27 KB 135.45 KB 35.81 KB 35.85 KB NODE_DEV
react-dom-unstable-fire.production.min.js 🔺+1.7% 🔺+1.5% 106.03 KB 107.81 KB 33.89 KB 34.4 KB NODE_PROD
react-dom-server.node.production.min.js 🔺+0.4% 🔺+0.3% 19.98 KB 20.06 KB 7.53 KB 7.55 KB NODE_PROD
react-dom.development.js +1.3% +1.1% 868.95 KB 879.98 KB 197.95 KB 200.18 KB UMD_DEV
react-dom-unstable-fire.profiling.min.js +1.6% +1.5% 109.46 KB 111.23 KB 34.79 KB 35.32 KB NODE_PROFILING
react-dom-server.browser.development.js +0.1% +0.1% 137.2 KB 137.38 KB 36.2 KB 36.24 KB UMD_DEV
react-dom.production.min.js 🔺+1.7% 🔺+1.2% 106.01 KB 107.79 KB 34.48 KB 34.91 KB UMD_PROD
ReactFire-dev.js +1.3% +1.1% 888.25 KB 899.56 KB 197.89 KB 200.16 KB FB_WWW_DEV
react-dom-server.browser.production.min.js 🔺+0.4% 🔺+0.5% 19.2 KB 19.28 KB 7.23 KB 7.26 KB UMD_PROD
react-dom.profiling.min.js +1.6% +1.2% 109.24 KB 111.01 KB 35.51 KB 35.94 KB UMD_PROFILING
ReactFire-prod.js 🔺+1.9% 🔺+1.3% 346.18 KB 352.76 KB 63.65 KB 64.5 KB FB_WWW_PROD
react-dom.development.js +1.3% +1.1% 863.3 KB 874.34 KB 196.38 KB 198.6 KB NODE_DEV
ReactFire-profiling.js +1.7% +1.3% 351.89 KB 357.92 KB 64.66 KB 65.53 KB FB_WWW_PROFILING
react-dom-server.browser.development.js +0.1% +0.1% 133.33 KB 133.51 KB 35.27 KB 35.3 KB NODE_DEV
react-dom.production.min.js 🔺+1.7% 🔺+1.5% 106.01 KB 107.8 KB 33.88 KB 34.4 KB NODE_PROD
react-dom-server.browser.production.min.js 🔺+0.4% 🔺+0.4% 19.12 KB 19.2 KB 7.22 KB 7.25 KB NODE_PROD
ReactDOM-prod.js 🔺+1.8% 🔺+1.3% 358.2 KB 364.78 KB 66.11 KB 66.99 KB FB_WWW_PROD
ReactDOMServer-prod.js 🔺+0.4% 🔺+0.3% 47.93 KB 48.12 KB 11.03 KB 11.07 KB FB_WWW_PROD
ReactDOM-profiling.js +1.7% +1.3% 365.17 KB 371.2 KB 67.35 KB 68.23 KB FB_WWW_PROFILING
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 60.43 KB 60.43 KB 15.73 KB 15.72 KB NODE_DEV
react-dom-unstable-fizz.node.development.js 0.0% -0.1% 3.88 KB 3.88 KB 1.51 KB 1.51 KB NODE_DEV
react-dom-unstable-fizz.node.production.min.js 0.0% 🔺+0.1% 1.1 KB 1.1 KB 667 B 668 B NODE_PROD
react-dom-test-utils.development.js 0.0% -0.0% 57.56 KB 57.56 KB 15.82 KB 15.82 KB UMD_DEV
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 3.81 KB 3.81 KB 1.54 KB 1.54 KB UMD_DEV
react-dom-test-utils.production.min.js 0.0% -0.0% 10.81 KB 10.81 KB 3.98 KB 3.97 KB UMD_PROD
react-dom-test-utils.development.js 0.0% -0.0% 55.9 KB 55.9 KB 15.5 KB 15.5 KB NODE_DEV
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 3.64 KB 3.64 KB 1.49 KB 1.49 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 🔺+0.1% 10.56 KB 10.56 KB 3.92 KB 3.92 KB NODE_PROD

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactART-dev.js +2.1% +2.0% 540.56 KB 551.87 KB 113.17 KB 115.48 KB FB_WWW_DEV
react-art.development.js +1.8% +1.7% 599.2 KB 610.2 KB 131.4 KB 133.56 KB UMD_DEV
react-art.production.min.js 🔺+1.8% 🔺+1.5% 97.15 KB 98.93 KB 29.92 KB 30.37 KB UMD_PROD
react-art.development.js +2.1% +1.9% 530.11 KB 541.15 KB 113.92 KB 116.13 KB NODE_DEV
react-art.production.min.js 🔺+2.9% 🔺+2.6% 62.13 KB 63.92 KB 19.2 KB 19.7 KB NODE_PROD
ReactART-prod.js 🔺+3.1% 🔺+2.6% 202.68 KB 208.93 KB 34.51 KB 35.41 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js +2.0% +1.9% 543.79 KB 554.82 KB 116.79 KB 119 KB UMD_DEV
react-test-renderer.production.min.js 🔺+2.8% 🔺+2.5% 63.46 KB 65.23 KB 19.64 KB 20.14 KB UMD_PROD
ReactShallowRenderer-dev.js +0.4% +0.4% 34.8 KB 34.95 KB 8.74 KB 8.78 KB FB_WWW_DEV
react-test-renderer.development.js +2.0% +1.9% 539.33 KB 550.36 KB 115.67 KB 117.88 KB NODE_DEV
react-test-renderer.production.min.js 🔺+2.8% 🔺+2.5% 63.15 KB 64.95 KB 19.43 KB 19.92 KB NODE_PROD
ReactTestRenderer-dev.js +2.1% +2.0% 551.88 KB 563.2 KB 115.7 KB 118 KB FB_WWW_DEV
react-test-renderer-shallow.development.js +0.4% +0.2% 41.93 KB 42.08 KB 10.86 KB 10.89 KB UMD_DEV
react-test-renderer-shallow.production.min.js 🔺+0.6% 🔺+0.6% 11.6 KB 11.67 KB 3.55 KB 3.57 KB UMD_PROD
react-test-renderer-shallow.development.js +0.4% +0.3% 36.07 KB 36.21 KB 9.49 KB 9.52 KB NODE_DEV
react-test-renderer-shallow.production.min.js 🔺+0.6% 🔺+0.3% 11.74 KB 11.81 KB 3.67 KB 3.68 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactFabric-dev.js +1.7% +1.6% 663.67 KB 674.99 KB 141.09 KB 143.4 KB RN_FB_DEV
ReactFabric-prod.js 🔺+2.8% 🔺+2.3% 245.47 KB 252.27 KB 42.53 KB 43.52 KB RN_FB_PROD
ReactNativeRenderer-dev.js +1.7% +1.6% 674.8 KB 686.12 KB 143.7 KB 146.02 KB RN_OSS_DEV
ReactFabric-profiling.js +2.3% +1.8% 253.91 KB 259.76 KB 44.19 KB 44.98 KB RN_FB_PROFILING
ReactNativeRenderer-prod.js 🔺+2.6% 🔺+2.0% 252.68 KB 259.38 KB 43.73 KB 44.62 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +2.4% +1.9% 261.09 KB 267.31 KB 45.43 KB 46.28 KB RN_OSS_PROFILING
ReactNativeRenderer-dev.js +1.7% +1.6% 674.9 KB 686.21 KB 143.76 KB 146.07 KB RN_FB_DEV
ReactNativeRenderer-prod.js 🔺+2.6% 🔺+2.0% 252.68 KB 259.37 KB 43.74 KB 44.63 KB RN_FB_PROD
ReactNativeRenderer-profiling.js +2.4% +1.9% 261.08 KB 267.3 KB 45.43 KB 46.28 KB RN_FB_PROFILING
ReactFabric-dev.js +1.7% +1.6% 663.57 KB 674.89 KB 141.05 KB 143.36 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+2.8% 🔺+2.3% 245.47 KB 252.27 KB 42.51 KB 43.51 KB RN_OSS_PROD
ReactFabric-profiling.js +2.3% +1.8% 253.91 KB 259.76 KB 44.18 KB 44.97 KB RN_OSS_PROFILING

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler-reflection.development.js +0.8% +0.4% 19.14 KB 19.29 KB 6.12 KB 6.15 KB NODE_DEV
react-reconciler-reflection.production.min.js 0.0% 🔺+0.2% 2.51 KB 2.51 KB 1.11 KB 1.12 KB NODE_PROD
react-reconciler-persistent.development.js +2.1% +2.0% 525.75 KB 536.79 KB 111.37 KB 113.58 KB NODE_DEV
react-reconciler-persistent.production.min.js 🔺+2.9% 🔺+2.6% 63.31 KB 65.17 KB 19.01 KB 19.51 KB NODE_PROD
react-reconciler.development.js +2.1% +2.0% 528.17 KB 539.2 KB 112.4 KB 114.62 KB NODE_DEV
react-reconciler.production.min.js 🔺+2.9% 🔺+2.6% 63.3 KB 65.16 KB 19 KB 19.51 KB NODE_PROD

Generated by 🚫 dangerJS

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.
@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Jun 18, 2019

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.

Copy link
Collaborator

@acdlite acdlite left a 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

@sebmarkbage sebmarkbage merged commit 76864f7 into facebook:master Jun 20, 2019
rickhanlonii pushed a commit to rickhanlonii/react that referenced this pull request Jun 25, 2019
* 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants