Explaining useQuery states
Tags: react
Published: 2023-04-10 08:53:29

In the world of React.js useQuery is well-known fetching tool with great [documentation](https://tanstack.com/query/latest/docs/react/overview). 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. 
```ts
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:
```ts
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.

```ts
  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

```ts
  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 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:

```ts
  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](https://tanstack.com/query/v3/docs/react/guides/dependent-queries) which has to wait for some another query before start loading

Continuation is here:

- [useQuery: isFetching vs isLoading](https://dev.to/altesack/usequery-isfetching-vs-isloading-mig)