I am a user who uses react. I didn't use the isloading function well while using react-query. In terms of ui, I thought that is loading was used to show the loading screen to the user. I thought I didn't have to show loading because the data fetch is fast.
But suddenly, this thought occurred to me.
If you hand over props to a child component, after the data passes 'undefined' and gets fetching, you often render the fetch data, so I thought it might be because of the reason for optimizing rendering, or is there another reason?
I think your question deserves a slightly more detailed answer because isFetching
is very often ignored.
As you’ve discovered, a query might load quickly enough that checking for isLoading
may seem unnecessary.
Many examples use this pattern:
const { data, isLoading } = useQuery(...);
if (isLoading)
return <div>Loading...</div>;
return <DataTable data={data} />;
While the data is being fetched the first time (UseQueryResult.status === "loading"
) you display a loading indicator and when the data is available you can use it as you please.
If the query is fast to load then you'll briefly see "Loading..." (or a spinner) and that's not a good UI. For very short delays, it's better to display nothing at all: you can do it with an animation (or setting a timer to make the spinner visible), it does not really matter as long as you embed this in your <LoadingIndicator />
component. I found that a 100/150 ms delay is a good compromise (for me). Out there there are studies to determine what the most appropriate value is, if you're interested.
You also want to handle errors, there is isError
for that (alternatively you can simply use status
for everything). A slightly more complicate implementation will look like this:
const { status, data, error, refresh } = useQuery(...);
if (status === "loading")
return <LoadingIndicator />;
if (status === "error")
return <Error error={error} onRetry={refresh} />
return <DataTable data={data} onRefresh={refresh} />;
Note how we introduced refresh()
, when we call it we will cause isFetching
to be true
(which does not happen for the initial fetch).
As we saw, isFetching
is true
when we already have a value (or attempted a first fetch) and we're fetching the same data again. This is true when we manually trigger a refresh but also when react-query is re-loading cached data (in this case status === "success"
).
This is very important in few cases:
isRefresh
is true
and data !== undefined
we may want to simply disable editing (where supported) without showing any loading indicator (or using a different subtler one).We might also want to display stale data using a subtle visual clue (for example dimming the text), if nothing changed then we'll have minimum flickering and layout changes when fresh data is available. See also isPreviousData
.
As you can see you will probably have a lot of boilerplate code around a simple query. What I like to do is to build one reusable component:
const query = useQuery(...);
return (
<Query query={query}>
({ data }) => <DataTable data={data} />
</Query>
);
Do not forget to include aria-live
and aria-busy
attributes, the outer container (parent for the data and the loading/error indicators) could be, for example, be defined like this:
<section aria-live="polite" aria-busy={isLoading || isFetching}>
Exact implementation depends on your specifics but do not forget to include ARIA attributes also for the loading indicator (especially if using animations/icons):
<div role="alert" aria-live="polite" aria-label="Loading" />
and for the error banner you may include role="alert"
(in this case the default aria-live="assertive"
implied by the "alert"
role is appropriate).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With