DEV Community

Cover image for Explaining useQuery states
Marat Latypov
Marat Latypov

Posted on

Explaining useQuery states

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,
  });
}

Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 to isLoading and isError 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>
Enter fullscreen mode Exit fullscreen mode

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)