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.
The recently introduced pager component renders three things:
However, the loading sequence appears to be extremely janky:
before.mov
This happens for three reasons:
The header changes size as we load data. So if we rendered anything below it (tab bar and/or content) while it was still a glimmer, we're gonna have to shift those things down when it fully loads. This feels jarring visually.
Our logic to position the content (so that it may fluidly scroll with the tabs staying sticky) relies on knowing both the header and the tab bar height. However, initially we don't know them at all, the header size changes when it turns from glimmer into content, and also React Native
onLayout
is always async so we can't rely on that being up-to-date anyway.As if that weren't enough, it seems like changing a scroll view padding on iOS on the fly can mess up the scroll position.
The Fix
First, let's enforce that content is revealed top down. First, we reveal the header glimmer. We wait for it to "settle" which depends on loading data. Since we can't use
<Suspense>
here (which is the canonical React solution to this problem), we track this manually. UntilisHeaderReady={true}
is passed, we don't try showing anything below to the user at all.In layout measurement code, I separated measuring "just" the header from measuring the tab bar. Previously we were measuring the header including the tab bar plus the tab bar separately. This made the calculation a bit confusing. Now we have two state variables that we can check for being zeroes. If either of them is zero, we're not ready to render the content.
As an optimization, we do render the tab bar a bit early with
opacity: 0
. So that we get its layout information early. We get away with it because its height is not going to change, unlike with the header.Finally, when we know the header has settled (
isHeaderReady={true}
) and we have bothheaderOnlyHeight
andtabBarHeight
calculated, we're going to render the content (this will include the content's glimmer if it hasn't loaded yet).Test Plan
Feeds:
after_feeds_feeds.mov
Lists:
after_lists.mov
Mod Lists:
after_mod_lists.mov
Renaming a list:
after_renaming.mov
There is a little jump when renaming that didn't get captured on the video. However I think we can live with it.
Also went through this on Android and web.