React How to Add Minimum Loading Delay
One of the tasks at work this past week was to show a spinner for a minimum duration when data fetching. Suppose you are data fetching and the promise resolves very quickly sometimes. We want to show a loading spinner to communicate to the user that the “recheck” button did do something, but we don’t want the loading animation to flicker or disappear uncomfortably quick. That is what this tutorial solves.
Assuming that you have a way to know whether the Promise result is still pending (i.e. a loading state that either you manage or is already managed), you can call the useThrottledValue hook from Mantine or the useThrottle hook from use-hooks to add a minimum delay when the variable changes. Unlike most solutions to this problem, the solution I’ve come up with has three crucial differences.
- Agnostic to the data fetching library/technique you use. This solution does not require you to modify how you data fetch. It only requires that you already have a loading state, which is a fair assumption since the problem calls for modifying the loading state!
- The loading state is not delayed further than the minimum duration. (i.e. if a Task takes
threshold + 100ms
to resolve, the delay will be equal tothreshold + 100ms
, and notthreshold + 100ms + delay
). For example, this blog post from 2019 (~6 years ago) that uses setTimeout without considering an async data fetch in-between. - The loading state is not delayed when being set to true. Although @iliasbhal first came up with the solution to control the delay outside of a library using a throttle hook, it would work not only when data fetching is completed but also when data fetching is initiated. This is an oversight in my opinion since from a UX perspective, we want to show the loading state immediately but want to delay early resolves. Of course wen the UI changes from empty → something, one can argue this solution isn’t necessary, but this tutorial is for those who already figured out they want a minimum loading delay.
// ...
const { loading: accountLoading, ... } = DATA_FETCHING_ABSTRACT; // something returned by TanStack's React Query or a custom useAsync hook
const accountLoadingDelayed = useThrottledValue(accountLoading, accountLoading ? 400 : 0);
// ... show loading animation in JSX
Yep the solution is this elegant.