-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Unify use
and renderDidSuspendDelayIfPossible
implementations
#25922
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
facebook-github-bot
added
CLA Signed
React Core Team
Opened by a member of the React Core Team
labels
Dec 22, 2022
acdlite
force-pushed
the
suspense-shell
branch
2 times, most recently
from
December 22, 2022 06:05
06f075d
to
2df0547
Compare
sizebot job is failing on recent PRs due to some auth issue |
sebmarkbage
approved these changes
Jan 3, 2023
This is part of a larger refactor I'm doing to simplify how this logic works, since it's currently spread out and duplicated across several different parts of the work loop. When a Suspense boundary shows a fallback, and it wasn't already showing a fallback, we mark the render as suspended. Knowing whether the in-progress tree includes a new fallback is useful for several reasons: we can choose to interrupt the current render and switch to a different task, we can suspend the work loop until more data becomes available, we can throttle the appearance of multiple fallback states, and so on. Currently this logic happens in the complete phase, but because we use renderDidSuspendWithDelay as a signal to interrupt the work loop, it's best to do it early as we possibly can. A subsequent step will move the logic even earlier, as soon as we attempt to unwrap an unresolved promise, so that `use` can determine whether it's OK to pause the entire work loop and wait for the promise. There's some existing code that attempts to do this but it doesn't have parity with how `renderDidSuspend` works, which is the main motivation for this series of refactors.
When unwrapping a promise with `use`, we sometimes suspend the work loop from rendering anything else until the data has resolved. This is different from how Suspense works in the old throw-a-promise world, where rather than suspend rendering midway through the render phase, we prepare a fallback and block the commit at the end, if necessary; however, the logic for determining whether it's OK to block is the same. The implementation is only incidentally different because it happens in two different parts of the code. This means for `use`, we end up doing the same checks twice, which is wasteful in terms of computataion, but also introduces a risk that the logic will accidentally diverage. This unifies the implementation by moving it into the SuspenseContext module. Most of the logic for deciding whether to suspend is already performed in the begin phase of SuspenseComponent, so it makes sense to store that information on the stack rather than recompute it on demand. The way I've chosen to model this is to track whether the work loop is rendering inside the "shell" of the tree. The shell is defined as the part of the tree that's visible in the current UI. Once we enter a new Suspense boundary (or a hidden Offscreen boundary, which acts a Suspense boundary), we're no longer in the shell. This is already how Suspense behavior was modeled in terms of UX, so using this concept directly in the implementation turns out to result in less code than before. For the most part, this is purely an internal refactor, though it does fix a bug in the `use` implementation related to nested Suspense boundaries. I wouldn't be surprised if it happens to fix other bugs that we haven't yet discovered, especially around Offscreen. I'll add more tests as I think of them.
acdlite
force-pushed
the
suspense-shell
branch
from
January 4, 2023 19:51
2df0547
to
7ee7066
Compare
github-actions bot
pushed a commit
that referenced
this pull request
Jan 4, 2023
…5922) When unwrapping a promise with `use`, we sometimes suspend the work loop from rendering anything else until the data has resolved. This is different from how Suspense works in the old throw-a-promise world, where rather than suspend rendering midway through the render phase, we prepare a fallback and block the commit at the end, if necessary; however, the logic for determining whether it's OK to block is the same. The implementation is only incidentally different because it happens in two different parts of the code. This means for `use`, we end up doing the same checks twice, which is wasteful in terms of computation, but also introduces a risk that the logic will accidentally diverge. This unifies the implementation by moving it into the SuspenseContext module. Most of the logic for deciding whether to suspend is already performed in the begin phase of SuspenseComponent, so it makes sense to store that information on the stack rather than recompute it on demand. The way I've chosen to model this is to track whether the work loop is rendering inside the "shell" of the tree. The shell is defined as the part of the tree that's visible in the current UI. Once we enter a new Suspense boundary (or a hidden Offscreen boundary, which acts a Suspense boundary), we're no longer in the shell. This is already how Suspense behavior was modeled in terms of UX, so using this concept directly in the implementation turns out to result in less code than before. For the most part, this is purely an internal refactor, though it does fix a bug in the `use` implementation related to nested Suspense boundaries. I wouldn't be surprised if it happens to fix other bugs that we haven't yet discovered, especially around Offscreen. I'll add more tests as I think of them. DiffTrain build for [c2d6552](c2d6552) [View git log for this commit](https://github.com/facebook/react/commits/c2d6552079178b36619f5dfd1ea39ae80b1d38b5)
facebook-github-bot
pushed a commit
to facebook/react-native
that referenced
this pull request
Jan 31, 2023
Summary: This sync includes the following changes: - **[48b687fc9](facebook/react@48b687fc9 )**: [trusted types][www] Add enableTrustedTypesIntegration flag back in ([#26016](facebook/react#26016)) //<an onion>// - **[9b1423cc0](facebook/react@9b1423cc0 )**: Revert "Hold host functions in var" ([#26079](facebook/react#26079)) //<Samuel Susla>// - **[ce09ace9a](facebook/react@ce09ace9a )**: Improve Error Messages when Access Client References ([#26059](facebook/react#26059)) //<Sebastian Markbåge>// - **[0652bdbd1](facebook/react@0652bdbd1 )**: Add flow types to Maps in ReactNativeViewConfigRegistry.js ([#26064](facebook/react#26064)) //<Samuel Susla>// - **[ee8509801](facebook/react@ee8509801 )**: [cleanup] remove deletedTreeCleanUpLevel feature flag ([#25529](facebook/react#25529)) //<Jan Kassens>// - **[0e31dd028](facebook/react@0e31dd028 )**: Remove findDOMNode www shim ([#25998](facebook/react#25998)) //<Jan Kassens>// - **[379dd741e](facebook/react@379dd741e )**: [www] set enableTrustedTypesIntegration to false ([#25997](facebook/react#25997)) //<Jan Kassens>// - **[555ece0cd](facebook/react@555ece0cd )**: Don't warn about concurrently rendering contexts if we finished rendering ([#22797](facebook/react#22797)) //<Sebastian Silbermann>// - **[0fce6bb49](facebook/react@0fce6bb49 )**: [cleanup] remove feature flags warnAboutDefaultPropsOnFunctionComponents and warnAboutStringRefs ([#25980](facebook/react#25980)) //<Jan Kassens>// - **[7002a6743](facebook/react@7002a6743 )**: [cleanup] remove unused values from ReactFeatureFlags.www-dynamic ([#25575](facebook/react#25575)) //<Jan Kassens>// - **[a48e54f2b](facebook/react@a48e54f2b )**: [cleanup] remove old feature flag warnAboutDeprecatedLifecycles ([#25978](facebook/react#25978)) //<Jan Kassens>// - **[0f4a83596](facebook/react@0f4a83596 )**: Remove duplicate JSResourceReferenceImpl mock ([#25976](facebook/react#25976)) //<Jan Kassens>// - **[c49131669](facebook/react@c49131669 )**: Remove unused Flow suppressions ([#25977](facebook/react#25977)) //<Jan Kassens>// - **[afe6521](facebook/react@afe6521e1 )**: Refactor: remove useless parameter ([#25923](facebook/react#25923)) //<Chris>// - **[34464fb16](facebook/react@34464fb16 )**: Upgrade to Flow 0.196.3 ([#25974](facebook/react#25974)) //<Jan Kassens>// - **[e2424f33b](facebook/react@e2424f33b )**: [flow] enable exact_empty_objects ([#25973](facebook/react#25973)) //<Jan Kassens>// - **[0b4f44302](facebook/react@0b4f44302 )**: [flow] enable enforce_local_inference_annotations ([#25921](facebook/react#25921)) //<Jan Kassens>// - **[0b974418c](facebook/react@0b974418c )**: [Fizz] Fork Fizz instruction set for inline script and external runtime ([#25862](facebook/react#25862)) //<mofeiZ>// - **[5379b6123](facebook/react@5379b6123 )**: Batch sync, default and continuous lanes ([#25700](facebook/react#25700)) //<Tianyu Yao>// - **[bbf4d2211](facebook/react@bbf4d2211 )**: Update import for babel-code-frame in build script ([#25963](facebook/react#25963)) //<Ming Ye>// - **[b83baf63f](facebook/react@b83baf63f )**: Transform updates to support Flow this annotation syntax ([#25918](facebook/react#25918)) //<Jan Kassens>// - **[c2d655207](facebook/react@c2d655207 )**: Unify `use` and `renderDidSuspendDelayIfPossible` implementations ([#25922](facebook/react#25922)) //<Andrew Clark>// - **[48274a43a](facebook/react@48274a43a )**: Remove vestigial Suspense batching logic ([#25861](facebook/react#25861)) //<Andrew Clark>// - **[de7d1c907](facebook/react@de7d1c907 )**: Add `fetchPriority` to `<img>` and `<link>` ([#25927](facebook/react#25927)) //<Steven>// - **[81d4ee9ca](facebook/react@81d4ee9ca )**: reconciler docs: fix small typo - "mode" (instead of "node") ([#25863](facebook/react#25863)) //<satelllte>// - **[5fcf1a4b4](facebook/react@5fcf1a4b4 )**: Bugfix: Synchronous ping during render phase sometimes unwinds the stack, leading to crash ([#25851](facebook/react#25851)) //<Andrew Clark>// - **[2b1fb91a5](facebook/react@2b1fb91a5 )**: ESLint upgrade to use hermes-eslint ([#25915](facebook/react#25915)) //<Jan Kassens>// - **[fabef7a6b](facebook/react@fabef7a6b )**: Resubmit Add HydrationSyncLane ([#25878](facebook/react#25878)) //<Tianyu Yao>// - **[7efa9e597](facebook/react@7efa9e597 )**: Fix unwinding context during selective hydration ([#25876](facebook/react#25876)) //<Tianyu Yao>// - **[84a0a171e](facebook/react@84a0a171e )**: Rename experimental useEvent to useEffectEvent ([#25881](facebook/react#25881)) //<Sebastian Markbåge>// - **[4dda96a40](facebook/react@4dda96a40 )**: [react-www] remove forked bundle ([#25866](facebook/react#25866)) //<Jan Kassens>// - **[9c09c1cd6](facebook/react@9c09c1cd6 )**: Revert "Fork ReactDOMSharedInternals for www ([#25791](facebook/react#25791))" ([#25864](facebook/react#25864)) //<lauren>// - **[996e4c0d5](facebook/react@996e4c0d5 )**: Offscreen add attach ([#25603](facebook/react#25603)) //<Samuel Susla>// - **[b14d7fa4b](facebook/react@b14d7fa4b )**: Add support for setNativeProps to Fabric ([#25737](facebook/react#25737)) //<Samuel Susla>// - **[819687279](facebook/react@819687279 )**: [Float] Fix typo in ReactDOMResourceValidation.js ([#25798](facebook/react#25798)) //<Ikko Ashimine>// - **[5dfc485f6](facebook/react@5dfc485f6 )**: fix tests for when float is off ([#25839](facebook/react#25839)) //<Josh Story>// - **[bfcbf3306](facebook/react@bfcbf3306 )**: toString children of title ([#25838](facebook/react#25838)) //<Sebastian Markbåge>// - **[d4bc16a7d](facebook/react@d4bc16a7d )**: Revert "[react-www] remove forked bundle" ([#25837](facebook/react#25837)) //<Ricky>// - **[d69b2cf82](facebook/react@d69b2cf82 )**: [bug fix] revert values in ReactFiberFlags to keep consistency for devtools ([#25832](facebook/react#25832)) //<Mengdi Chen>// - **[645ae2686](facebook/react@645ae2686 )**: [react-www] remove forked bundle ([#25831](facebook/react#25831)) //<Jan Kassens>// - **[d807eb52c](facebook/react@d807eb52c )**: Revert recent hydration changes ([#25812](facebook/react#25812)) //<Andrew Clark>// - **[2ccfa657d](facebook/react@2ccfa657d )**: Fork ReactDOMSharedInternals for www ([#25791](facebook/react#25791)) //<lauren>// - **[f0534ae94](facebook/react@f0534ae94 )**: Avoid replaying SelectiveHydrationException in dev ([#25754](facebook/react#25754)) //<Tianyu Yao>// - **[7fab379d8](facebook/react@7fab379d8 )**: fix link to ReactDOMHostconfig in reconciler docs ([#25788](facebook/react#25788)) //<Dmitry>// - **[500c8aa08](facebook/react@500c8aa08 )**: Add component name to StrictMode error message ([#25718](facebook/react#25718)) //<Samuel Susla>// - **[353c30252](facebook/react@353c30252 )**: Hold host functions in var ([#25741](facebook/react#25741)) //<Samuel Susla>// Changelog: [General][Changed] - React Native sync for revisions 17f6912...48b687f jest_e2e[run_all_tests] Reviewed By: rubennorte Differential Revision: D42855483 fbshipit-source-id: c244a595bb2d490a23b333c1b16d04a459ec94fc
kassens
added a commit
to kassens/react
that referenced
this pull request
Mar 20, 2023
…ions (facebook#25922)" This reverts commit c2d6552.
kassens
added a commit
to kassens/react
that referenced
this pull request
Mar 20, 2023
…ions (facebook#25922)" This reverts commit c2d6552.
kassens
added a commit
to kassens/react
that referenced
this pull request
Mar 20, 2023
…ions (facebook#25922)" This reverts commit c2d6552.
acdlite
added a commit
to acdlite/react
that referenced
this pull request
Mar 22, 2023
Fixes a bug in SuspenseList that @kassens found when deploying React to Meta. In some scenarios, SuspenseList would force the fallback of a deeply nested Suspense boundary into fallback mode, which should never happen under any circumstances — SuspenseList should only affect the nearest descendent Suspense boundaries, without going deeper. The cause was that the internal ForceSuspenseFallback context flag was not being properly reset when it reached the nearest Suspense boundary. It should only be propagated shallowly. We didn't discover this earlier because the scenario where it happens is not that common. To trigger the bug, you need to insert a new Suspense boundary into an already-mounted row of the list. But often when a new Suspense boundary is rendered, it suspends and shows a fallback, anyway, because its content hasn't loaded yet. Another reason we didn't discover this earlier is because there was another bug that was accidentally masking it, which was fixed by facebook#25922. When that fix landed, it revealed this bug. The SuspenseList implementation is complicated but I'm not too concerned with the current messiness. It's an experimental API, and we intend to release it soon, but there are some known flaws and missing features that we need to address first regardless. We'll likely end up rewriting most of it. Co-Authored-By: Jan Kassens <jkassens@meta.com>
acdlite
added a commit
to acdlite/react
that referenced
this pull request
Mar 22, 2023
Fixes a bug in SuspenseList that @kassens found when deploying React to Meta. In some scenarios, SuspenseList would force the fallback of a deeply nested Suspense boundary into fallback mode, which should never happen under any circumstances — SuspenseList should only affect the nearest descendent Suspense boundaries, without going deeper. The cause was that the internal ForceSuspenseFallback context flag was not being properly reset when it reached the nearest Suspense boundary. It should only be propagated shallowly. We didn't discover this earlier because the scenario where it happens is not that common. To trigger the bug, you need to insert a new Suspense boundary into an already-mounted row of the list. But often when a new Suspense boundary is rendered, it suspends and shows a fallback, anyway, because its content hasn't loaded yet. Another reason we didn't discover this earlier is because there was another bug that was accidentally masking it, which was fixed by facebook#25922. When that fix landed, it revealed this bug. The SuspenseList implementation is complicated but I'm not too concerned with the current messiness. It's an experimental API, and we intend to release it soon, but there are some known flaws and missing features that we need to address first regardless. We'll likely end up rewriting most of it. Co-Authored-By: Jan Kassens <jkassens@meta.com>
acdlite
added a commit
that referenced
this pull request
Mar 22, 2023
Fixes a bug in SuspenseList that @kassens found when deploying React to Meta. In some scenarios, SuspenseList would force the fallback of a deeply nested Suspense boundary into fallback mode, which should never happen under any circumstances — SuspenseList should only affect the nearest descendent Suspense boundaries, without going deeper. The cause was that the internal ForceSuspenseFallback context flag was not being properly reset when it reached the nearest Suspense boundary. It should only be propagated shallowly. We didn't discover this earlier because the scenario where it happens is not that common. To trigger the bug, you need to insert a new Suspense boundary into an already-mounted row of the list. But often when a new Suspense boundary is rendered, it suspends and shows a fallback, anyway, because its content hasn't loaded yet. Another reason we didn't discover this earlier is because there was another bug that was accidentally masking it, which was fixed by #25922. When that fix landed, it revealed this bug. The SuspenseList implementation is complicated but I'm not too concerned with the current messiness. It's an experimental API, and we intend to release it soon, but there are some known flaws and missing features that we need to address first regardless. We'll likely end up rewriting most of it. Co-authored-by: Jan Kassens <jkassens@meta.com>
github-actions bot
pushed a commit
that referenced
this pull request
Mar 22, 2023
Fixes a bug in SuspenseList that @kassens found when deploying React to Meta. In some scenarios, SuspenseList would force the fallback of a deeply nested Suspense boundary into fallback mode, which should never happen under any circumstances — SuspenseList should only affect the nearest descendent Suspense boundaries, without going deeper. The cause was that the internal ForceSuspenseFallback context flag was not being properly reset when it reached the nearest Suspense boundary. It should only be propagated shallowly. We didn't discover this earlier because the scenario where it happens is not that common. To trigger the bug, you need to insert a new Suspense boundary into an already-mounted row of the list. But often when a new Suspense boundary is rendered, it suspends and shows a fallback, anyway, because its content hasn't loaded yet. Another reason we didn't discover this earlier is because there was another bug that was accidentally masking it, which was fixed by #25922. When that fix landed, it revealed this bug. The SuspenseList implementation is complicated but I'm not too concerned with the current messiness. It's an experimental API, and we intend to release it soon, but there are some known flaws and missing features that we need to address first regardless. We'll likely end up rewriting most of it. Co-authored-by: Jan Kassens <jkassens@meta.com> DiffTrain build for [51a7c45](51a7c45)
OlimpiaZurek
pushed a commit
to OlimpiaZurek/react-native
that referenced
this pull request
May 22, 2023
Summary: This sync includes the following changes: - **[48b687fc9](facebook/react@48b687fc9 )**: [trusted types][www] Add enableTrustedTypesIntegration flag back in ([facebook#26016](facebook/react#26016)) //<an onion>// - **[9b1423cc0](facebook/react@9b1423cc0 )**: Revert "Hold host functions in var" ([facebook#26079](facebook/react#26079)) //<Samuel Susla>// - **[ce09ace9a](facebook/react@ce09ace9a )**: Improve Error Messages when Access Client References ([facebook#26059](facebook/react#26059)) //<Sebastian Markbåge>// - **[0652bdbd1](facebook/react@0652bdbd1 )**: Add flow types to Maps in ReactNativeViewConfigRegistry.js ([facebook#26064](facebook/react#26064)) //<Samuel Susla>// - **[ee8509801](facebook/react@ee8509801 )**: [cleanup] remove deletedTreeCleanUpLevel feature flag ([facebook#25529](facebook/react#25529)) //<Jan Kassens>// - **[0e31dd028](facebook/react@0e31dd028 )**: Remove findDOMNode www shim ([facebook#25998](facebook/react#25998)) //<Jan Kassens>// - **[379dd741e](facebook/react@379dd741e )**: [www] set enableTrustedTypesIntegration to false ([facebook#25997](facebook/react#25997)) //<Jan Kassens>// - **[555ece0cd](facebook/react@555ece0cd )**: Don't warn about concurrently rendering contexts if we finished rendering ([facebook#22797](facebook/react#22797)) //<Sebastian Silbermann>// - **[0fce6bb49](facebook/react@0fce6bb49 )**: [cleanup] remove feature flags warnAboutDefaultPropsOnFunctionComponents and warnAboutStringRefs ([facebook#25980](facebook/react#25980)) //<Jan Kassens>// - **[7002a6743](facebook/react@7002a6743 )**: [cleanup] remove unused values from ReactFeatureFlags.www-dynamic ([facebook#25575](facebook/react#25575)) //<Jan Kassens>// - **[a48e54f2b](facebook/react@a48e54f2b )**: [cleanup] remove old feature flag warnAboutDeprecatedLifecycles ([facebook#25978](facebook/react#25978)) //<Jan Kassens>// - **[0f4a83596](facebook/react@0f4a83596 )**: Remove duplicate JSResourceReferenceImpl mock ([facebook#25976](facebook/react#25976)) //<Jan Kassens>// - **[c49131669](facebook/react@c49131669 )**: Remove unused Flow suppressions ([facebook#25977](facebook/react#25977)) //<Jan Kassens>// - **[afe6521](facebook/react@afe6521e1 )**: Refactor: remove useless parameter ([facebook#25923](facebook/react#25923)) //<Chris>// - **[34464fb16](facebook/react@34464fb16 )**: Upgrade to Flow 0.196.3 ([facebook#25974](facebook/react#25974)) //<Jan Kassens>// - **[e2424f33b](facebook/react@e2424f33b )**: [flow] enable exact_empty_objects ([facebook#25973](facebook/react#25973)) //<Jan Kassens>// - **[0b4f44302](facebook/react@0b4f44302 )**: [flow] enable enforce_local_inference_annotations ([facebook#25921](facebook/react#25921)) //<Jan Kassens>// - **[0b974418c](facebook/react@0b974418c )**: [Fizz] Fork Fizz instruction set for inline script and external runtime ([facebook#25862](facebook/react#25862)) //<mofeiZ>// - **[5379b6123](facebook/react@5379b6123 )**: Batch sync, default and continuous lanes ([facebook#25700](facebook/react#25700)) //<Tianyu Yao>// - **[bbf4d2211](facebook/react@bbf4d2211 )**: Update import for babel-code-frame in build script ([facebook#25963](facebook/react#25963)) //<Ming Ye>// - **[b83baf63f](facebook/react@b83baf63f )**: Transform updates to support Flow this annotation syntax ([facebook#25918](facebook/react#25918)) //<Jan Kassens>// - **[c2d655207](facebook/react@c2d655207 )**: Unify `use` and `renderDidSuspendDelayIfPossible` implementations ([facebook#25922](facebook/react#25922)) //<Andrew Clark>// - **[48274a43a](facebook/react@48274a43a )**: Remove vestigial Suspense batching logic ([facebook#25861](facebook/react#25861)) //<Andrew Clark>// - **[de7d1c907](facebook/react@de7d1c907 )**: Add `fetchPriority` to `<img>` and `<link>` ([facebook#25927](facebook/react#25927)) //<Steven>// - **[81d4ee9ca](facebook/react@81d4ee9ca )**: reconciler docs: fix small typo - "mode" (instead of "node") ([facebook#25863](facebook/react#25863)) //<satelllte>// - **[5fcf1a4b4](facebook/react@5fcf1a4b4 )**: Bugfix: Synchronous ping during render phase sometimes unwinds the stack, leading to crash ([facebook#25851](facebook/react#25851)) //<Andrew Clark>// - **[2b1fb91a5](facebook/react@2b1fb91a5 )**: ESLint upgrade to use hermes-eslint ([facebook#25915](facebook/react#25915)) //<Jan Kassens>// - **[fabef7a6b](facebook/react@fabef7a6b )**: Resubmit Add HydrationSyncLane ([facebook#25878](facebook/react#25878)) //<Tianyu Yao>// - **[7efa9e597](facebook/react@7efa9e597 )**: Fix unwinding context during selective hydration ([facebook#25876](facebook/react#25876)) //<Tianyu Yao>// - **[84a0a171e](facebook/react@84a0a171e )**: Rename experimental useEvent to useEffectEvent ([facebook#25881](facebook/react#25881)) //<Sebastian Markbåge>// - **[4dda96a40](facebook/react@4dda96a40 )**: [react-www] remove forked bundle ([facebook#25866](facebook/react#25866)) //<Jan Kassens>// - **[9c09c1cd6](facebook/react@9c09c1cd6 )**: Revert "Fork ReactDOMSharedInternals for www ([facebook#25791](facebook/react#25791))" ([facebook#25864](facebook/react#25864)) //<lauren>// - **[996e4c0d5](facebook/react@996e4c0d5 )**: Offscreen add attach ([facebook#25603](facebook/react#25603)) //<Samuel Susla>// - **[b14d7fa4b](facebook/react@b14d7fa4b )**: Add support for setNativeProps to Fabric ([facebook#25737](facebook/react#25737)) //<Samuel Susla>// - **[819687279](facebook/react@819687279 )**: [Float] Fix typo in ReactDOMResourceValidation.js ([facebook#25798](facebook/react#25798)) //<Ikko Ashimine>// - **[5dfc485f6](facebook/react@5dfc485f6 )**: fix tests for when float is off ([facebook#25839](facebook/react#25839)) //<Josh Story>// - **[bfcbf3306](facebook/react@bfcbf3306 )**: toString children of title ([facebook#25838](facebook/react#25838)) //<Sebastian Markbåge>// - **[d4bc16a7d](facebook/react@d4bc16a7d )**: Revert "[react-www] remove forked bundle" ([facebook#25837](facebook/react#25837)) //<Ricky>// - **[d69b2cf82](facebook/react@d69b2cf82 )**: [bug fix] revert values in ReactFiberFlags to keep consistency for devtools ([facebook#25832](facebook/react#25832)) //<Mengdi Chen>// - **[645ae2686](facebook/react@645ae2686 )**: [react-www] remove forked bundle ([facebook#25831](facebook/react#25831)) //<Jan Kassens>// - **[d807eb52c](facebook/react@d807eb52c )**: Revert recent hydration changes ([facebook#25812](facebook/react#25812)) //<Andrew Clark>// - **[2ccfa657d](facebook/react@2ccfa657d )**: Fork ReactDOMSharedInternals for www ([facebook#25791](facebook/react#25791)) //<lauren>// - **[f0534ae94](facebook/react@f0534ae94 )**: Avoid replaying SelectiveHydrationException in dev ([facebook#25754](facebook/react#25754)) //<Tianyu Yao>// - **[7fab379d8](facebook/react@7fab379d8 )**: fix link to ReactDOMHostconfig in reconciler docs ([facebook#25788](facebook/react#25788)) //<Dmitry>// - **[500c8aa08](facebook/react@500c8aa08 )**: Add component name to StrictMode error message ([facebook#25718](facebook/react#25718)) //<Samuel Susla>// - **[353c30252](facebook/react@353c30252 )**: Hold host functions in var ([facebook#25741](facebook/react#25741)) //<Samuel Susla>// Changelog: [General][Changed] - React Native sync for revisions 17f6912...48b687f jest_e2e[run_all_tests] Reviewed By: rubennorte Differential Revision: D42855483 fbshipit-source-id: c244a595bb2d490a23b333c1b16d04a459ec94fc
bigfootjon
pushed a commit
that referenced
this pull request
Apr 18, 2024
Fixes a bug in SuspenseList that @kassens found when deploying React to Meta. In some scenarios, SuspenseList would force the fallback of a deeply nested Suspense boundary into fallback mode, which should never happen under any circumstances — SuspenseList should only affect the nearest descendent Suspense boundaries, without going deeper. The cause was that the internal ForceSuspenseFallback context flag was not being properly reset when it reached the nearest Suspense boundary. It should only be propagated shallowly. We didn't discover this earlier because the scenario where it happens is not that common. To trigger the bug, you need to insert a new Suspense boundary into an already-mounted row of the list. But often when a new Suspense boundary is rendered, it suspends and shows a fallback, anyway, because its content hasn't loaded yet. Another reason we didn't discover this earlier is because there was another bug that was accidentally masking it, which was fixed by #25922. When that fix landed, it revealed this bug. The SuspenseList implementation is complicated but I'm not too concerned with the current messiness. It's an experimental API, and we intend to release it soon, but there are some known flaws and missing features that we need to address first regardless. We'll likely end up rewriting most of it. Co-authored-by: Jan Kassens <jkassens@meta.com> DiffTrain build for commit 51a7c45.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When unwrapping a promise with
use
, we sometimes suspend the work loop from rendering anything else until the data has resolved. This is different from how Suspense works in the old throw-a-promise world, where rather than suspend rendering midway through the render phase, we prepare a fallback and block the commit at the end, if necessary; however, the logic for determining whether it's OK to block is the same. The implementation is only incidentally different because it happens in two different parts of the code. This means foruse
, we end up doing the same checks twice, which is wasteful in terms of computation, but also introduces a risk that the logic will accidentally diverge.This unifies the implementation by moving it into the SuspenseContext module. Most of the logic for deciding whether to suspend is already performed in the begin phase of SuspenseComponent, so it makes sense to store that information on the stack rather than recompute it on demand.
The way I've chosen to model this is to track whether the work loop is rendering inside the "shell" of the tree. The shell is defined as the part of the tree that's visible in the current UI. Once we enter a new Suspense boundary (or a hidden Offscreen boundary, which acts a Suspense boundary), we're no longer in the shell. This is already how Suspense behavior was modeled in terms of UX, so using this concept directly in the implementation turns out to result in less code than before.
For the most part, this is purely an internal refactor, though it does fix a bug in the
use
implementation related to nested Suspense boundaries. I wouldn't be surprised if it happens to fix other bugs that we haven't yet discovered, especially around Offscreen. I'll add more tests as I think of them.