React-query series Part 3: Data fetching with the useQuery hook.
Tags: react, javascript, webdev, hooks
Published: 2021-08-22 19:07:05

> Cover image by [**Lawrence Eagles**](https://blog.logrocket.com/author/lawrenceoputa/) in the article: [**What’s new in React Query 3**](https://blog.logrocket.com/whats-new-in-react-query-3/)


Hey everyone :wave:

I'm back again. I am always grateful for the encouragement. Thank you for the :sparkling_heart:, for taking the time to read the lengthy [**Part Two: QueryClient configuration**](https://dev.to/nnajiforemma10/react-query-series-part-2-queryclient-configuration-18g6) of this series. To my new followers; Thank you! I'll do my best not to disappoint you.

In part two, we talked about overriding some of the defaults that come with `react-query` by setting our custom defaults for both `queries` and `mutations`. We set our custom defaults on such options like `retry` , `staleTime`, `cacheTime`, `refecthOnMount` and a couple of others for our `queries` and `retry` for `mutations`.

## Table of contents

  * [Intro](#point-0)
  * [The useQuery hook](#point-1)
    * [Fetching data](#point-1a)
    * [Passing variables to query functions](#point-1b)
    * [Overriding query defaults](#point-1c)
  * [Conclusion](#point-2)

## Intro <a name="point-0"></a>
In this part, we will be learning how to fetch data from an API using the `useQuery` hook. I promised to show you how we can override the defaults we set earlier, so we will have a look at that too. Always remember to use the table of contents above to jump to sections relevant to you.

## The useQuery hook <a name="point-1"></a>
We start by installing [axios](https://www.npmjs.com/package/axios) and refactoring a bit.
```
npm i axios
``` 
Our `QueryClient` goes to a new file `./src/util/queryClient.js`

```javascript
import { QueryClient} from 'react-query';

const queryClientConfig = {
    defaultOptions: {
      queries: {
        retry: 2,
        staleTime: 1000 * 30,// 30 seconds
        cacheTime: 1000 * 30, //30 seconds
        refetchOnMount: "always",
        refetchOnWindowFocus: "always",
        refetchOnReconnect: "always",
        refetchInterval: 1000 * 30, //30 seconds
        refetchIntervalInBackground: false,
        suspense: false,
        
      },
      mutations: {
        retry: 2,
      },
    },
 
 export const queryClient = new QueryClient(queryClientConfig);

```
If you just got here, we explained this snippet [**here**](https://dev.to/nnajiforemma10/react-query-series-part-2-queryclient-configuration-18g6)

We clean our `App.js` thus
```javascript
import { QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import { queryClient } from "./util/queryClient";

 
 function App() {
   return  (
           <QueryClientProvider client={queryClient}>
             {/* The rest of your application */}
             <ReactQueryDevtools initialIsOpen={false} />
           </QueryClientProvider>
        )
 }
``` 
We will also create a `queryKeys.js` file at `./src/util/queryKeys.js`.
This file will host all the query keys for our application. 

```javascript
export const fetchPostsKey = "FETCH_POSTS";
```
Create a `fetchPosts.service.js` file at `./src/services/fetchPosts.service.js` and create your simple async function to fetch a list of posts.
We will be using the [JSONPlaceholder](https://jsonplaceholder.typicode.com) REST API for this demo.

```javascript
import axios from "axios";

/**
 * @desc fetch a list of posts
 */
export const fetchPosts = async () => {
  const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`);
  return res?.data;
};
```
### Fetching data <a name="point-1a"></a>

Create a `Posts.js` component at `./src/components/Posts.js`

Remember to import your `Posts.js` component to your `App.js`

```javascript
...
 
 function App() {
   return  (
            <QueryClientProvider client={queryClient}>
              <Posts/>
              <ReactQueryDevtools initialIsOpen={false} />
            </QueryClientProvider>
        )
 }
``` 


`Posts.js`

```javascript
import { useQuery } from "react-query";
import { fetchPosts } from "../../services/fetchPosts.service";
import { fetchPostsKey } from "../../util/queryKeys";

const Posts = () => {
  
  const {  isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery(fetchPostsKey, fetchPosts);
  

  return (
    <div>
      {isLoading ? (
        <div>Loading...</div>
      ) : isError ? (
        <div>An error while fetching posts</div>
      ) : (
        data?.map((post) => (
          <div key={post?.id}>
            <div>{post?.title}</div>
            <div>{post?.body}</div>
          </div>
        ))
      )}
    </div>
  );
};
export default Posts;
```
The `useQuery` hook accepts a query key as its first argument and the query function as its's second argument.
The query key is required, the query function also is required as there was not default query function defined for our `queries` in `QueryClient`. Let's take a quick brush at the items in the destructured object `useQuery` returns to us.

`isLoading`: It is a `boolean` value that returns `true` when the query has no data and is currently fetching and `false` when not. 

`isError`: Also a `boolean` value. It return `true` when the query attempt results in an error.

`isSuccess`: Returns `true` if the query has received a response with no errors and is ready to display its data. `isSuccess` is `false` when query is not yet resolved or results in an error.

`refetch` : This is a function that manually refetches the query.

`remove` : This function is used to manually remove the query from cache.

`data` :  It is the response from the last successful query. `data` will be `undefined` if query fails for the first time.

`error` :  It is the error response from your query. It is defined when your query is in an `isError` state.

The `useQuery` hook returns more values in the destructured object than described here, but this few I chose for the scope of this article. You can read more about the `useQuery` hook [**here**] (https://react-query.tanstack.com/reference/useQuery).


### Passing variable(s) to a query function <a name="point-1b"></a>
So, what if you want to pass a variable or variables to your query function? E.g. you have a function that fetches a single post and it requires you to pass in a post `id`; What do you do?
Let's see how it is done.


We will a new key entry in a `queryKeys.js` file at `./src/util/queryKeys.js`.


```javascript
...
export const fetchSinglePostKey = "FETCH_SINGLE_POST";
```

Create also a `fetchSinglePost.service.js` file at `./src/services/fetchSinglePost.service.js` and create your simple async function to fetch a single post by `id`.

`fetchSinglePost.service.js`
```javascript
import axios from "axios";

/**
 * @desc fetches a single post
 */
export const fetchSinglePost = async ({queryKey}) => {
const [_key, id] = queryKey
const res = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
  return res?.data;
};
```
Post.js
```javascript
import { useQuery } from "react-query";
import { fetchSinglePost } from "../../services/fetchSinglePost .service";
import { fetchSinglePostKey } from "../../util/queryKeys";

const Post = () => {
  
// fetching the post with the id of 1

  const {  isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery([fetchSinglePostKey, 1], fetchSinglePost );
  

  return (
    <div>
      {isLoading ? (
        <div>Loading...</div>
      ) : isError ? (
        <div>An error while fetching post</div>
      ) : (
          <div >
            <div>{data?.title}</div>
            <div>{data?.body}</div>
          </div>
        )
      )}
    </div>
  );
};
export default Post;
```

Here, we are no more using a `string` value for our query key but an `array`, passing in the query string first and the post `id` as required by our query function `fetchSinglePost.service.js`.

The `fetchSinglePost` function declared in `useQuery` hook is passed in a context, this context has `queryKey` `array` nested in it. This `queryKey` array contains your query string as the first item in the array and your `id` variable for fetching our single post.

Remember to import your `Post.js` component to your `App.js`

```javascript
...
 
 function App() {
   return  (
            <QueryClientProvider client={queryClient}>
               ...
              <Post/>
              <ReactQueryDevtools initialIsOpen={false} />
            </QueryClientProvider>
        )
 }
``` 



### Overriding query defaults <a name="point-1c"></a>

We have seen two demonstrations using `useQuery` but don't forget that they all are operating within the `queries` default we set earlier. To override some of the global configurations, we pass an object as a third argument to the `useQuery` hook. Every option you declare a new value, that option is overridden for that `useQuery` instance only.

```javascript
...
 const {  isLoading, isError, isSuccess, refetch, remove, data, error} = useQuery([fetchSinglePostKey, 1], fetchSinglePost, {
    refetchInterval : 3* 1000 //3 seconds
});

...
```
What this snippet above implies is that, although we configured `react-query` globally to refetch queries every 30 seconds, This particular query will refetch every 3 seconds; breaking away from the global configurations.

## Conclusion<a name="point-2"></a> 
The returned data from our queries are persisted in a cache. In the next part, we will discuss how the to interact with this cache.
Thank you all for your support. If you are beginner and haven't written something, do that today! Please give me a :sparkling_heart: if this post or part of it has helped you. Comments are welcomed too.
Follow me on [twitter @NnajioforEmma10
](https://twitter.com/NnajioforEmma10)

## Table of contents

  * [Intro](#point-0)
  * [The useQuery hook](#point-1)
    * [Fetching data](#point-1a)
    * [Passing variables to query functions](#point-1b)
    * [Overriding query defaults](#point-1c)
  * [Conclusion](#point-2)