Skip to content

Navigation Menu

Sign in
Sign up

Maximum time to display stale content in a transition? #4

Answered by rickhanlonii
devongovett asked this question in Q&A
Discussion options

I was playing with the demo on https://async-react.dev/, and came across a question that others might have as well. I noticed that if you make the network slow and then click another tab (like "Done"), the tab updates instantly (great), but the content in the list stays stale until the request completes. There's a loading indicator on the optimistically selected tab, but it's not clear that the content in the list below is not actually the content for that tab.

I assume this is to prevent flickering on fast networks where the suspense boundary shows its fallback for a very short amount of time, but on slow networks it's a little strange. Ideally I think the stale content from the previous tab would stay there for a short amount of time, but if the request took a while, it should fall back to a skeleton again. Is there a recommended pattern to implement something like that?

You can set a key={tab} on the Suspense to force the fallback to appear, but then it shows even on fast networks. I couldn't find a clean way to show the suspense fallback only after a minimum amount of time, which I think would be the best UX.

You must be logged in to vote

I wouldn't revert the content to the fallback (though you could by just bumping the key in a setTimeout).

I think there are a few better options:

  • Fade the content: After some amount of time (maybe the same as the tab glimmer), fade the content on the current page, or overlay a gimmer over the list. This allows the content to remain visible, and prevents the content for switching to the fallback right as you're about to click. You can use the optimistic/transition pending state for this. The longer you expect the data to delay loading, the more attractive this option is because it gives the user something to do while things are loading, instead of staring at fallbacks all day.
  • Always show...

Replies: 1 comment

Comment options

I wouldn't revert the content to the fallback (though you could by just bumping the key in a setTimeout).

I think there are a few better options:

  • Fade the content: After some amount of time (maybe the same as the tab glimmer), fade the content on the current page, or overlay a gimmer over the list. This allows the content to remain visible, and prevents the content for switching to the fallback right as you're about to click. You can use the optimistic/transition pending state for this. The longer you expect the data to delay loading, the more attractive this option is because it gives the user something to do while things are loading, instead of staring at fallbacks all day.
  • Always show a fallback, but prefetch: The cases where you want to show a fallback would be for things that are conceptually different pages. In the example app, I intentionally chose tabs that are "filters" for the same data, but if the tabs were going to conceptually different tabs of the app, then you may want to show a glimmer.
    • For this case, there are a couple ways to avoid the glimmer. In all of these cases, the glimmer is debounced by exactly the speed of the network (instead of some static amount like 1000ms, and the data comes in at 1001ms).
      • Prefetching on hover to warm the cache: you can trigger the request in the hover event, so that by the time it renders the data is already there. Hover is especially good at this because it already takes a few hundred milliseconds between hover and click, which is enough time to complete the request on fast networks. This doesn't work on all platforms (like mobile), but it works in many (like VR, which actually has an even longer delay between hover and click because of the distance of the physical controller trigger)
      • Prefetching with <Activity />: You can also use <Activity /> to pre-fetch the tabs. This can be based on a heuristic like most commonly used, or recently visited. This can be combined with hover: on hover -> render a hidden Activity -> on click -> switch to visible.
      • Prefetching in the Action: Less common, but one trick I've done is to add a await Promise.race([prefetch, timeout(50)]) in the async transition add a small delay to the Action before navigating. You have to be careful here because it can cause flickering. But that will hold back the transition to give it time to prefetch, in cases where you can't just do it in hover, can't prefetch, and have no other options but doing it on click.
You must be logged in to vote
0 replies
Answer selected by rickhanlonii
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /