📚 RTK Query: Understanding useQuery Hook
Tags: react, redux, javascript, tutorial
Published: 2023-11-29 20:24:02

## Introduction
It's been a while since I last wrote about RTK Query. I realized some people left questions in my previous blog about RTK Query, so I decided to write more about it. Hopefully, it will be helpful for some people. I want to share my deep understanding of the `useQuery` hook, specifically in this [documentation](https://redux-toolkit.js.org/rtk-query/api/created-api/hooks#usequery).

![useQuery](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgx7pm8inu2a31zyi7oz.png)

> If you haven't read my previous blog, I recommend to start reading the [📚 RTK Query Tutorial (CRUD)](https://dev.to/raaynaldo/rtk-query-tutorial-crud-51hl). I will refer to that in this blog

{% embed https://dev.to/raaynaldo/rtk-query-tutorial-crud-51hl %}

## UseQuery

> A React hook that automatically triggers fetches of data from an endpoint, 'subscribes' the component to the cached data, and reads the request status and cached data from the Redux store. The component will re-render as the loading status changes and the data becomes available.

That is a description according to the RTK Query docs. From my perspective, I just think about fetching, but it's not a simple fetching. RTK Query gives us many features rather than just a simple fetch. I already talked about this in my previous blog. Once again, go there before going through this blog! Sorry another reminder to make sure you are not lost here.

Let's go to the main reason that I wrote this blog. In my previous blog, I had my repository to give you an example of how to use `useQuery`

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page);
```
https://github.com/raaynaldo/rtk-query-setup-tutorial/blob/main/src/components/Albums.js#L11-L17

It's just some options and results.

The full feature of `useQuery` is like this
```jsx
  const {
    data: albums = [],
    originalArgs,
    currentData,
    requestId,
    endpointName,
    startedTimeStamp,
    fulfilledTimeStamp,
    isUninitialized,
    isLoading,
    isFetching,
    isError,
    error,
    refetch,
  } = useGetAlbumsQuery(page, {
    skip: true,
    selectFromResult: (result) => result,
    pollingInterval: 5000,
    refetchOnFocus: true,
    refetchOnReconnect: true,
    refetchOnMountOrArgChange: true,
  });
```
That's a lot right? I'm pretty sure, we will not use all of these at the same time. I just want to share their purposes, so you can utilize them in the future.

Before I explain the useQueryOptions in the useQuery, I should let you know that some options such as `refetchOnFocus`, `refetchOnReconnect`, and `refetchOnMountOrArgChange` could be use as `createApi` parameters as well. Check the docs [here](https://redux-toolkit.js.org/rtk-query/api/createApi).
If we are using `createAPI`, all the `useQuery` from the API will follow those configurations.

## 🟠 UseQueryOptions
This is the second parameter of the `useQuery` when you call it in your component. 
```tsx
useGetAlbumsQuery(arg: any, option: UseQueryOptions)
```
keep in mind that the options are an object, so you can use them like this.

```jsx
 useGetAlbumsQuery(page, {
    skip: true,
    selectFromResult: (result) => result,
    pollingInterval: 5000,
    refetchOnFocus: true,
    refetchOnReconnect: true,
    refetchOnMountOrArgChange: true,
  })
```
Let's delve into every attribute on the object.

> All code examples that I use will refer to this [file](https://github.com/raaynaldo/rtk-query-setup-tutorial/blob/main/src/components/Albums.js#L11-L17), so you feel free to explore the project

### 🔸 skip?: boolean
The default is `false`.
If we set the skip to true, the fetching will not run at first, until the skip is updated to false.
```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    skip: true,
  });
```

Maybe for the real-life use case, we don't want to fetch the data if the argument is not ready yet. We can utilize the skip to be like this.
```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    skip: page === undefined,
  });
```

In the next GIF, I added a new state called `skip`, so I can modify it in react dev tools.

![Skip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8fstebntyywf7vx0hg2.gif)

Technically, if the skip is true, it will either not start the fetch or will remove the data that already has been fetched. Just want to be clear, if the data that has been fetched is hidden because this skip is true, and you update the skip to be false, it will only call the cache data. I proofed this because the `requestId` is the same. I will explain more about `requestId` later.

![skip-requestId](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyw9ndlr2gkqpwdclf9o.gif)


### 🔸 selectFromResult?: boolean
It's one of the interesting options here, we can do more flexibility here. This could override the `useQueryResult` (which I'll explain later in the next section).


I give an example of how could I update the result data to be upper case.

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    selectFromResult: (result) => {

      result.data = result.data.map((album) => ({
        ...album,
        title: album.title.toUpperCase(),
      }));

      return result;
    },
  });
```

One thing you need to make sure that if you want to utilize the the result value from RTK Query, you must return the original result. That's why I just update the `result.data`, so I can still use the `data`, `isLoading`, 'isFetching', 'isError', and 'error'

Also, you can create another option if you add a new attribute to the return value of the function. To be honest, I'm not sure if it's a good practice or not, but it works.

Try to update with this.

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
    newResult,
  } = useGetAlbumsQuery(page, {
    selectFromResult: (result) => {
      result.data = result.data?.map((album) => ({
        ...album,
        title: album.title.toUpperCase(),
      }));

      result.newResult = 'test newResult';
      return result;
    },
  });

  console.log({ newResult });
```


### 🔸 pollingInterval?: number
Be cautious when using this option. It will cost a lot of API fetch requests. It will tell the redux toolkit to refetch your API on specify time. The number for this `pollingInterval` refers to time in milliseconds.

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    pollingInterval: 2000,
  });
```

![polling interval](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rs1pisradhi9gnwfhvot.gif)

If you see my data is a little bit flashy, it's a quick loading component that is showing. I make my `useQuery` refetch the data for every 2 seconds.

### 🔸 refetchOnFocus?: boolean
Another option to refetch the data rather than using time interval, we can utilize this feature, which depends on the user interactions. When the users is focused on the browser or tab, and they come back to the page it will fetch the data again from the API.

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    refetchOnFocus: true,
  });
```

![refetchOnFocus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exjkjcv4ilm2t9ycqxj7.gif)


### 🔸 refetchOnReconnect?: boolean
Similar to the `refetchOnFocus`, it's just a different trigger of the refetch. It will do the refetch if the browser is disconnected from the internet and reconnect again to the internet. How to test? Attempt to disconnect from your Wi-Fi connection.

```jsx
  const {
    data: albums = [],
    isLoading,
    isFetching,
    isError,
    error,
  } = useGetAlbumsQuery(page, {
    refetchOnReconnect: true,
  });
```

![refetchOnReconnect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czoxbk0t2dly2ccbx3wi.gif)

### 🔸 refetchOnMountOrArgChange?: boolean
I should apologize for not fully understanding how this option works. If you have insights into this feature, please leave a comment. 🙏

## 🔵 UseQueryResult
It's the result of calling the useQuery function. It may not be necessary to use all of its features.

Additionally, you can see mostly the result from the redux dev tools extension as well. Download the extension here.
Google Chrome: https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?pli=1
Mozilla Firefox: https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/ 

![redux dev tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oo7f03fawgpr4ml5n3ny.png)

### 🔹 data
It's the result of the fetch data.
```jsx
  const {
    data: album = [],
  } = useGetAlbumsQuery(page);
```

If you're wondering why I renamed and assigned a default value to data as `data: album = []`

In order to differentiate different useQuery data, I prefer to rename it rather than data, and it could be ambiguous with another query result.

For default value using an empty array, makes our code to be less complicated when we want to render the data. We don't need to add any conditional render or even an [optional chaining (?.)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

```jsx
{albums.map((album) => ( ... ))}
```

### 🔹 isLoading
it's a boolean status that refers to the first fetch. Keep in mind it's only before the data is existed until the first fetch. It's different with `isFetching`
```jsx
  const {
    isLoading,
  } = useGetAlbumsQuery(page);
```
### 🔹 isFetching
This is different from the `isLoading` because this status depends on the refetching situation. For example, if the data is already fetched for the first time, and RTK Query does refetch to update the cache, it will refer to this status.
```jsx
  const {
    isFetching,
  } = useGetAlbumsQuery(page);
```

If you remember some options that I explained before, such as `refetchOnFocus`, `refetchOnReconnect`, and `pollingInterval`. It will affect the `isFetching`, not the `isLoading`

![isFetching](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ctqjexlsbyeecnm6giu.gif)

This is why I handle loading with these two statuses.
```jsx
  if (isLoading || isFetching) {
    return <div>loading...</div>;
  }
```

### 🔹 isSuccess
Status for showing the fetching or refetching is successful. it is opposite of `isError` status.

```jsx
  const {
    isSuccess,
  } = useGetAlbumsQuery(page);
```

### 🔹 isError
Status for showing the fetching or refetching is failed or error. it opposites of `isSuccess` status. I usually utilize this status for handling the error and showing the error message.

```jsx
  const {
    isError,
  } = useGetAlbumsQuery(page);
```

![error and success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5shx4rlw1zxcfwvxzmj7.png)
The screenshot illustrates that isError and isSuccess will consistently differ.

### 🔹 error
The error response that comes from the API.

```jsx
  const {
    error,
  } = useGetAlbumsQuery(page);
```

This is how I utilize `isError` and `error` results.
```jsx
  if (isError) {
    console.log({ error });
    return <div>{error.status}</div>;
  }
```

### 🔹 refetch
It's an interesting feature, so we can run refect whenever we want. I believe it can be used for refreshing the data. If you need to refresh the data when the user clicks a button. You can utilize this function.

```jsx
  const {
    refetch,
  } = useGetAlbumsQuery(page);
```

I created a new button for this refetch feature.
```jsx
      <button disabled={isError} onClick={refetch}>
        Refetch
      </button>
```

![refetch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2sglccrqrtioa7h91gww.gif)

### 🔹 requested
I assume this attribute is not necessary to be used in your project. However, I want to point out about invalidating the cache. The `requestId` will be updated to be a different id if RTK query runs a refetch or update the cache.

```jsx
  const {
    requestId,
  } = useGetAlbumsQuery(page);
```

![requestId](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfgutcl9rpyt5cuwub6q.gif)
What I did to simulate the GIF
1. Go to the next page
2. Back to the previous page
3. Go to the next page (Same request ID with number 1)
4. Back to the next page (Same request ID with number 1)
5. Click Refetch button(new request Id is generated)
6. Click Refetch button(new request Id is generated)
7. Click Refetch button(new request Id is generated)


### 🔹 The Rest of Result Attributes
I believe that the above attributes that I already explained are the most important attributes that we can implement for real-life scenarios. Other attributes could be useful in your case, but I believe it's easy to understand if you go to the [docs](https://redux-toolkit.js.org/rtk-query/api/created-api/hooks#usequery).


## Conclusion
In summary, learning about the `useQuery` hook in RTK Query has shown us lots of cool things beyond just getting data. The UseQueryOptions allows us to control how fetching works, like skipping the first fetch, changing the data after getting it, and even fetching again based on time or user actions. The UseQueryResult attributes, like data, isLoading, and isError, help us know what's happening with our fetch in real time, making it easier to build user-friendly apps. Even though some things, like `requestId`, might seem tricky, they're useful for certain situations, like updating stored data. So, as we finish up, remember that understanding useQuery in RTK Query opens up new possibilities for making efficient React apps. Feel free to explore more in the documentation to see what fits best for your projects! Happy Reading, and leave a comment if you have questions. 📚