In the world of React.js useQuery is well-known fetching tool with great documentation. However, often developers have some problems with status understanding, so I had to clarify it for myself.
Preparing
Before we start research useQuery statuses we should have some hook using it. Let's create one.
interface Quote {
id: number,
quote: string,
author: string,
}
export const useRandomQuote = (): UseQueryResult<Quote> => {
const url ='https://dummyjson.com/quotes/random';
const fetchData = (): Promise<Quote> =>
axios.get(url).then<Quote>(response => response.data);
return useQuery<Quote>({
queryKey: ['quote'],
queryFn: fetchData,
});
}
In my example I used API to get random quote. Or course it's just an example, it could be any other API.
Using the hook
Let's try use it now:
export const SomeComponent = (): JSX.Element => {
const result = useRandomQuote();
// result.data can be undefined
return <h1>{result.data?.quote}</h1>
}
Here we are fetching some random quote and rendering it. The hook returns UseQueryResult
type, which has data
field with our quote, which has type Quote
.
The point is that at the beginning data probably is not loaded yet, so it could be undefined. That's why we have to use result.data?.quote
instead of result.data.quote
.
And here is a good place for a placeholder.
const result = useRandomQuote();
if(result.isLoading) {
return <h1>Loading ...</h1>
}
// but result.data still can be undefined
return <h1>{result.data?.quote}</h1>
Ok. We handled isLoading status, but result data still can be undefined. It's because there could be a error during data fetch.
Let's add some check
const result = useRandomQuote();
if(result.isLoading) {
return <h1>Loading ...</h1>
}
if(result.isError) {
return <h1>Error ...</h1>
}
return <h1>{result.data.quote}</h1>
And now data
is defined and we can safely use it. Cheers!
If you use useQuery ver.3 you probably should handle
isIdle
flag in addition toisLoading
andisError
to be sure that the data is properly defined.
Suppose you have some error handler, in this case you probably don't want to handle error in components. In this case you can use isSuccess
flag:
const result = useRandomQuote();
if(!result.isSuccess) {
// We use isSuccess flag
// So we have to deal with "loading" and "error" statuses here
// But we handle here only "loading" case here,
// Because the "error" case is catched somewhere else
return <h1>Loading ...</h1>
}
return <h1>{result.data.quote}</h1>
It looks the problem is solved now!
Query States summary
Let's look closer at the states of useQuery
Loading state | Success state |
---|---|
This is the query state while data is loading | This is the query state when data has been successfully fetched |
isLoading: true | isSuccess: true |
data: undefined | data: is fetched, defined and could be safely used |
In addition there are two error states
It's a bit complicated. Sometimes useQuery fetches data, and sometimes re-fetch it. So error could be thrown in two cases
Loading error state | Refetch error state |
---|---|
isError: true | isError: true |
isLoadingError: true | isRefetchError: true |
data: undefined | data: is fetched before and cached, so it could be safely used |
Idle state should be noted.
This state had place in useQuery version 3 and removed in version 4.
It was about state of dependent query which has to wait for some another query before start loading
Continuation is here:
Top comments (0)