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
Unhide Suspense trees without entanglement #18733
Conversation
Details of bundled changes.Comparing: ac533fd...9ea9c5f react-dom
react-cache
Size changes (experimental) |
Details of bundled changes.Comparing: ac533fd...9ea9c5f react-dom
react
ReactDOM: size: 0.0%, gzip: -0.0% React: size: 0.0%, gzip: 0.0% Size changes (stable) |
64ac432
to
f8c42fb
Compare
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 9ea9c5f:
|
f8c42fb
to
e7e3288
Compare
e7e3288
to
c40d206
Compare
@@ -3384,6 +3379,10 @@ function beginWork( | |||
return null; | |||
} | |||
} | |||
case OffscreenComponent: { | |||
pushRenderExpirationTime(workInProgress, renderExpirationTime); |
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.
Could optimize this slightly by pushing only when the component is hidden
e9c2d07
to
3d841d7
Compare
When a Suspense boundary is in its fallback state, you cannot switch back to the main content without also finishing any updates inside the tree that might have been skipped. That would be a form of tearing. Before we fixed this in facebook#18411, the way this bug manifested was that a boundary was suspended by an update that originated from a child component (as opposed to props from a parent). While the fallback was showing, it received another update, this time at high priority. React would render the high priority update without also including the original update. That would cause the fallback to switch back to the main content, since the update that caused the tree to suspend was no longer part of the render. But then, React would immediately try to render the original update, which would again suspend and show the fallback, leading to a momentary flicker in the UI. The approach added in facebook#18411 is, when receiving a high priority update to a Suspense tree that's in its fallback state is to bail out, keep showing the fallback and finish the update in the rest of the tree. After that commits, render again at the original priority. Because low priority expiration times are inclusive of higher priority expiration times, this ensures that all the updates are committed together. The new approach in this commit is to turn `renderExpirationTime` into a context-like value that lives on the stack. Then, when unhiding the Suspense boundary, we can push a new `renderExpirationTime` that is inclusive of both the high pri update and the original update that suspended. Then the boundary can be unblocked in a single render pass. An advantage of the old approach is that by deferring the work of unhiding, there's less work to do in the high priority update. The key advantage of the new approach is that it solves the consistency problem without having to entangle the entire root.
3d841d7
to
9ea9c5f
Compare
Closed by #18782 |
When a Suspense boundary is in its fallback state, you cannot switch back to the main content without also finishing any updates inside the tree that might have been skipped. That would be a form of tearing.
Before we fixed this in #18411, the way this bug manifested was that a boundary was suspended by an update that originated from a child component (as opposed to props from a parent). While the fallback was showing, it received another update, this time at high priority. React would render the high priority update without also including the original update. That would cause the fallback to switch back to the main content, since the update that caused the tree to suspend was no longer part of the render. But then, React would immediately try to render the original update, which would again suspend and show the fallback, leading to a momentary flicker in the UI.
The approach added in #18411 is, when receiving a high priority update to a Suspense tree that's in its fallback state is to bail out, keep showing the fallback and finish the update in the rest of the tree. After that commits, render again at the original priority. Because low priority expiration times are inclusive of higher priority expiration times, this ensures that all the updates are committed together.
The new approach in this commit is to turn
renderExpirationTime
into a context-like value that lives on the stack. Then, when unhiding the Suspense boundary, we can push a newrenderExpirationTime
that is inclusive of both the high pri update and the original update that suspended. Then the boundary can be unblocked in a single render pass.An advantage of the old approach is that by deferring the work of unhiding, there's less work to do in the high priority update.
The key advantage of the new approach is that it solves the consistency problem without having to entangle the entire root.