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

[Bugfix] Dropped updates inside a suspended tree #18384

Merged
merged 6 commits into from
Mar 26, 2020

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Mar 25, 2020

Fixes #18353.

When there are multiple updates at different priority levels inside a suspended subtree, all but the highest priority one is dropped after the highest one suspends.

We do have tests that cover this for updates that originate outside of the Suspense boundary, but not for updates that originate inside.

I'm surprised it's taken us this long to find this issue, but it makes sense in that transition updates usually originate outside the boundary or "seam" of the part of the UI that is transitioning.


This is related to #18353 but it only fixes part of it. There's another bug, which I believe is related to setState traversal skipping over the primary fragment fiber. Still working on that one. Update: yep, that was it. Pushed another commit that and confirmed that it fixes the sandbox in #18353. CodeSandbox CI is currently down, but when it's back up I'll post an updated sandbox.

Also related to #18357, but likewise only fixes part of it.

@facebook-github-bot facebook-github-bot added React Core Team Opened by a member of the React Core Team CLA Signed labels Mar 25, 2020
Comment on lines 1612 to 1614
// TODO: We used to track the lowest pending level for the whole root, but
// we removed it to simplify the implementation. It might be worth adding
// it back for this use case, to avoid redundant passes.
Copy link
Collaborator Author

@acdlite acdlite Mar 25, 2020

Choose a reason for hiding this comment

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

@sebmarkbage I think I'm going to do this (add back root.lastPendingTime)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of tracking it globally, why won't we just track it on this suspense boundary in the state?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm going to track the "suspended time" locally but to track the "last pending time" locally I'd have to add stuff to the setState path. Doesn't seem worth it since the bitmask version will be much simpler.

@sizebot
Copy link

sizebot commented Mar 25, 2020

Details of bundled changes.

Comparing: e0ab1a4...ae27d3a

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js +0.7% +0.8% 569.07 KB 572.92 KB 118.18 KB 119.08 KB UMD_DEV
react-test-renderer.production.min.js 🔺+0.5% 🔺+0.6% 73.09 KB 73.47 KB 22.29 KB 22.41 KB UMD_PROD
react-test-renderer-shallow.development.js 0.0% 0.0% 38.4 KB 38.4 KB 9.3 KB 9.3 KB UMD_DEV
react-test-renderer.development.js +0.7% +0.8% 542.39 KB 546.11 KB 116.77 KB 117.66 KB NODE_DEV
react-test-renderer.production.min.js 🔺+0.5% 🔺+0.7% 72.9 KB 73.29 KB 21.97 KB 22.11 KB NODE_PROD
ReactTestRenderer-dev.js +0.7% +0.8% 574.7 KB 578.63 KB 121.16 KB 122.1 KB FB_WWW_DEV

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.profiling.min.js +0.3% +0.3% 126.22 KB 126.63 KB 39.52 KB 39.64 KB NODE_PROFILING
ReactDOM-dev.js +0.5% +0.4% 957.07 KB 961.39 KB 212.74 KB 213.69 KB FB_WWW_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.1% 1.19 KB 1.19 KB 699 B 698 B UMD_PROD
ReactDOM-prod.js 🔺+0.6% 🔺+0.4% 396.75 KB 399.09 KB 72.18 KB 72.49 KB FB_WWW_PROD
ReactDOM-profiling.js +0.6% +0.4% 414.34 KB 416.75 KB 75.19 KB 75.49 KB FB_WWW_PROFILING
react-dom-test-utils.development.js 0.0% 0.0% 59.24 KB 59.24 KB 15.65 KB 15.65 KB UMD_DEV
ReactDOMTesting-dev.js +0.5% +0.5% 913.79 KB 918.12 KB 203.96 KB 204.91 KB FB_WWW_DEV
ReactDOMTesting-prod.js 🔺+0.5% 🔺+0.3% 391.41 KB 393.27 KB 71.53 KB 71.77 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% 0.0% 54.7 KB 54.7 KB 15.03 KB 15.04 KB NODE_DEV
ReactDOMTesting-profiling.js +0.5% +0.3% 391.41 KB 393.27 KB 71.53 KB 71.77 KB FB_WWW_PROFILING
react-dom.development.js +0.4% +0.5% 933.4 KB 937.6 KB 203.81 KB 204.74 KB UMD_DEV
react-dom.production.min.js 🔺+0.3% 🔺+0.2% 122.09 KB 122.5 KB 39.04 KB 39.14 KB UMD_PROD
ReactDOMForked-dev.js +0.5% +0.4% 957.07 KB 961.39 KB 212.74 KB 213.69 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.32 KB 10.32 KB 3.49 KB 3.5 KB UMD_PROD
react-dom.profiling.min.js +0.3% +0.3% 125.92 KB 126.34 KB 40.28 KB 40.42 KB UMD_PROFILING
ReactDOMForked-prod.js 🔺+0.6% 🔺+0.4% 396.75 KB 399.09 KB 72.18 KB 72.49 KB FB_WWW_PROD
react-dom.development.js +0.5% +0.5% 888.6 KB 892.67 KB 201.33 KB 202.25 KB NODE_DEV
ReactDOMForked-profiling.js +0.6% +0.4% 414.34 KB 416.75 KB 75.19 KB 75.49 KB FB_WWW_PROFILING
react-dom.production.min.js 🔺+0.3% 🔺+0.3% 122.24 KB 122.65 KB 38.24 KB 38.36 KB NODE_PROD
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.05 KB 10.05 KB 3.38 KB 3.38 KB NODE_PROD

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactART-dev.js +0.8% +0.8% 573.58 KB 577.91 KB 120.2 KB 121.17 KB FB_WWW_DEV
ReactART-prod.js 🔺+1.0% 🔺+0.7% 231.91 KB 234.15 KB 39.56 KB 39.84 KB FB_WWW_PROD
react-art.development.js +0.6% +0.7% 651.84 KB 656.04 KB 136.62 KB 137.52 KB UMD_DEV
react-art.production.min.js 🔺+0.4% 🔺+0.4% 108.6 KB 109.01 KB 32.93 KB 33.06 KB UMD_PROD
react-art.development.js +0.7% +0.8% 555.27 KB 559.34 KB 118.98 KB 119.89 KB NODE_DEV
react-art.production.min.js 🔺+0.6% 🔺+0.6% 73.59 KB 73.99 KB 22.08 KB 22.21 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js +0.6% +0.6% 652.37 KB 656.31 KB 140.72 KB 141.61 KB RN_FB_DEV
ReactNativeRenderer-prod.js 🔺+0.8% 🔺+0.7% 266.86 KB 268.98 KB 46.36 KB 46.67 KB RN_FB_PROD
ReactNativeRenderer-profiling.js +0.8% +0.6% 278.65 KB 280.82 KB 48.62 KB 48.93 KB RN_FB_PROFILING
ReactFabric-dev.js +0.6% +0.7% 631.52 KB 635.46 KB 135.96 KB 136.87 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+0.8% 🔺+0.7% 258.7 KB 260.81 KB 44.85 KB 45.16 KB RN_OSS_PROD
ReactFabric-profiling.js +0.8% +0.6% 270.49 KB 272.66 KB 47.11 KB 47.4 KB RN_OSS_PROFILING
ReactFabric-dev.js +0.6% +0.7% 634.24 KB 638.17 KB 136.3 KB 137.19 KB RN_FB_DEV
ReactFabric-prod.js 🔺+0.8% 🔺+0.7% 258.85 KB 260.97 KB 44.88 KB 45.19 KB RN_FB_PROD
ReactNativeRenderer-dev.js +0.6% +0.6% 649.66 KB 653.6 KB 140.37 KB 141.27 KB RN_OSS_DEV
ReactFabric-profiling.js +0.8% +0.6% 270.64 KB 272.8 KB 47.13 KB 47.43 KB RN_FB_PROFILING
ReactNativeRenderer-prod.js 🔺+0.8% 🔺+0.7% 266.71 KB 268.83 KB 46.33 KB 46.64 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +0.8% +0.6% 278.51 KB 280.67 KB 48.59 KB 48.9 KB RN_OSS_PROFILING

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.7% +0.7% 595.64 KB 599.71 KB 125.29 KB 126.21 KB NODE_DEV
react-reconciler.production.min.js 🔺+0.5% 🔺+0.5% 78.82 KB 79.23 KB 23.21 KB 23.34 KB NODE_PROD

Size changes (experimental)

Generated by 🚫 dangerJS against ae27d3a

@sizebot
Copy link

sizebot commented Mar 25, 2020

Details of bundled changes.

Comparing: e0ab1a4...ae27d3a

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.production.min.js 🔺+0.3% 🔺+0.3% 118.17 KB 118.55 KB 37.13 KB 37.26 KB NODE_PROD
react-dom-test-utils.development.js 0.0% 0.0% 59.23 KB 59.23 KB 15.64 KB 15.64 KB UMD_DEV
ReactDOMTesting-profiling.js +0.5% +0.4% 403.86 KB 406.01 KB 73.47 KB 73.77 KB FB_WWW_PROFILING
react-dom.profiling.min.js +0.3% +0.3% 122.02 KB 122.41 KB 38.32 KB 38.43 KB NODE_PROFILING
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.1% 1.17 KB 1.17 KB 691 B 690 B UMD_PROD
ReactDOMForked-dev.js +0.4% +0.4% 1002.92 KB 1007.25 KB 222.78 KB 223.73 KB FB_WWW_DEV
ReactDOMForked-prod.js 🔺+0.5% 🔺+0.4% 422.61 KB 424.77 KB 76.68 KB 77 KB FB_WWW_PROD
react-dom.development.js +0.4% +0.5% 903.61 KB 907.46 KB 198.59 KB 199.49 KB UMD_DEV
ReactDOMForked-profiling.js +0.5% +0.4% 440.25 KB 442.48 KB 79.77 KB 80.06 KB FB_WWW_PROFILING
react-dom.production.min.js 🔺+0.3% 🔺+0.4% 118.09 KB 118.48 KB 37.88 KB 38.03 KB UMD_PROD
react-dom.profiling.min.js +0.3% +0.3% 121.8 KB 122.19 KB 39.12 KB 39.22 KB UMD_PROFILING
ReactDOMTesting-dev.js +0.5% +0.5% 940.55 KB 944.88 KB 209.86 KB 210.81 KB FB_WWW_DEV
react-dom.development.js +0.4% +0.4% 860.04 KB 863.77 KB 196.17 KB 197.04 KB NODE_DEV
ReactDOMTesting-prod.js 🔺+0.5% 🔺+0.4% 403.86 KB 406.01 KB 73.47 KB 73.77 KB FB_WWW_PROD
react-dom-server.browser.development.js 0.0% -0.0% 126.32 KB 126.32 KB 33.84 KB 33.83 KB NODE_DEV
ReactDOM-dev.js +0.4% +0.4% 1002.92 KB 1007.25 KB 222.78 KB 223.73 KB FB_WWW_DEV
ReactDOM-prod.js 🔺+0.5% 🔺+0.4% 422.61 KB 424.77 KB 76.68 KB 77 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% 0.0% 54.69 KB 54.69 KB 15.03 KB 15.03 KB NODE_DEV
ReactDOM-profiling.js +0.5% +0.4% 440.25 KB 442.48 KB 79.77 KB 80.06 KB FB_WWW_PROFILING
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.31 KB 10.31 KB 3.49 KB 3.49 KB UMD_PROD
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.04 KB 10.04 KB 3.37 KB 3.37 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js +0.6% +0.6% 649.65 KB 653.59 KB 140.37 KB 141.27 KB RN_OSS_DEV
ReactNativeRenderer-prod.js 🔺+0.8% 🔺+0.7% 266.7 KB 268.82 KB 46.32 KB 46.63 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +0.8% +0.6% 278.5 KB 280.66 KB 48.58 KB 48.89 KB RN_OSS_PROFILING
ReactFabric-dev.js +0.6% +0.7% 631.51 KB 635.45 KB 135.96 KB 136.86 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+0.8% 🔺+0.7% 258.68 KB 260.8 KB 44.85 KB 45.16 KB RN_OSS_PROD
ReactFabric-profiling.js +0.8% +0.6% 270.48 KB 272.64 KB 47.1 KB 47.39 KB RN_OSS_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.7% +0.8% 535.8 KB 539.52 KB 115.29 KB 116.18 KB NODE_DEV
react-art.production.min.js 🔺+0.5% 🔺+0.8% 71.08 KB 71.47 KB 21.4 KB 21.57 KB NODE_PROD
ReactART-dev.js +0.7% +0.8% 601.65 KB 605.98 KB 125.87 KB 126.85 KB FB_WWW_DEV
ReactART-prod.js 🔺+0.9% 🔺+0.7% 240.95 KB 243.07 KB 40.97 KB 41.26 KB FB_WWW_PROD
react-art.development.js +0.6% +0.7% 631.57 KB 635.42 KB 132.95 KB 133.82 KB UMD_DEV
react-art.production.min.js 🔺+0.4% 🔺+0.3% 106.04 KB 106.43 KB 32.2 KB 32.3 KB UMD_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer-shallow.development.js 0.0% 0.0% 38.39 KB 38.39 KB 9.29 KB 9.29 KB UMD_DEV
react-test-renderer.development.js +0.7% +0.8% 542.36 KB 546.09 KB 116.76 KB 117.65 KB NODE_DEV
react-test-renderer.production.min.js 🔺+0.5% 🔺+0.7% 72.88 KB 73.26 KB 21.95 KB 22.09 KB NODE_PROD
ReactTestRenderer-dev.js +0.7% +0.8% 574.68 KB 578.62 KB 121.15 KB 122.09 KB FB_WWW_DEV
react-test-renderer.development.js +0.7% +0.8% 569.04 KB 572.89 KB 118.17 KB 119.07 KB UMD_DEV
react-test-renderer.production.min.js 🔺+0.5% 🔺+0.6% 73.06 KB 73.44 KB 22.27 KB 22.39 KB UMD_PROD

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.6% +0.7% 573.83 KB 577.56 KB 121.08 KB 121.98 KB NODE_DEV
react-reconciler-reflection.development.js 0.0% 0.0% 15.55 KB 15.55 KB 4.73 KB 4.73 KB NODE_DEV
react-reconciler.production.min.js 🔺+0.5% 🔺+0.6% 75.92 KB 76.31 KB 22.5 KB 22.64 KB NODE_PROD

Size changes (stable)

Generated by 🚫 dangerJS against ae27d3a

//
// As a workaround, we need to recompute the `childExpirationTime`
// by bubbling it up from the next level of children. This is
// based on similar logic in `resetChildExpirationTime`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

An alternative fix would be to detect this during setState. I thought it might be better to localize the fix here since it's relatively rare but I'm having second thoughts.

Copy link
Collaborator

@sebmarkbage sebmarkbage Mar 25, 2020

Choose a reason for hiding this comment

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

I think this is the right call to keep it in the rare path.

I think we should just bite the bullet have have Suspense boundaries always have these fragments even when not suspended. It's too much code to manage (buggy and byte size). Especially with enter/exit animations it's going to get even trickier.

It adds a bit more memory but there's not that many suspense boundaries and the ones that do exist fairly often transition through this state anyway.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That would definitely simplify things a lot

@gaearon
Copy link
Collaborator

gaearon commented Mar 25, 2020

Is it worth adding tests for similar scenarios but with updates originating inside the fallback just in case?

Adds a `resolveText` method as an alternative to using timers. Also
removes dependency on react-cache (for just this one test file; can do
the others later).

Timer option is still there if you provide a `ms` prop.
When there are multiple updates at different priority levels inside
a suspended subtree, all but the highest priority one is dropped after
the highest one suspends.

We do have tests that cover this for updates that originate outside of
the Suspense boundary, but not for updates that originate inside.

I'm surprised it's taken us this long to find this issue, but it makes
sense in that transition updates usually originate outside the boundary
or "seam" of the part of the UI that is transitioning.
Fixes a bug where updates inside a suspended tree are dropped because
the fragment fiber we insert to wrap the hidden children is not part of
the return path, so it doesn't get marked during setState.

As a workaround, I recompute `childExpirationTime` right before deciding
to bail out by bubbling it up from the next level of children.

This is something we should consider addressing when we refactor the
Fiber data structure.
This reverts commit 9a54113.

I want to use this so we can check if there might be any lower priority
updates in a suspended tree.

We can remove it again during the expiration times refactor.
We don't currently have an mechanism to check if there are lower
priority updates in a subtree, but we can check if there are any in the
whole root. This still isn't perfect but it's better than using Idle,
which frequently leads to redundant re-renders.

When we refactor `expirationTime` to be a bitmask, this will no longer
be necessary because we'll know exactly which "task bits" remain.
@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 26, 2020

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 ae27d3a:

Sandbox Source
nervous-nightingale-o3tdv Configuration
great-waterfall-z2wlu Issue #18353

@acdlite
Copy link
Collaborator Author

acdlite commented Mar 26, 2020

Added a fallback test case

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.

Bug: Updates in the primary tree only unsuspend once
5 participants