Asynchronous data fetching using React Query ⚛️
Tags: react, javascript, webdev, tutorial
Published: 2020-09-26 04:03:06

Querying data is not that hard right? Yes, we first render the component. We write the fetch method. We call that fetch data in the component’s onMount. We then store the data into the state and then render the data. What if the query fails. We either retry or give up right? Even to retry we need to call the refetch method again and then again check if we resolved the data. And if we update something we need to re-render that particular component based on the data. So many queries and functions.

![](https://www.skcript.com/svrmedia/heroes/image3-7.gif)

Well in this article we will combine all these steps into a single step and make our data querying easily by using an awesome **react hook library** called [ReactQuery](https://github.com/tannerlinsley/react-query) by [Tanner Linsey](https://github.com/tannerlinsley).

Let us first set up our React application. For this article I will be using [this](https://github.com/SoorajSNBlaze333/react-javascript-bootstrap-starter) starter kit template. After cloning the template we will start with the react query installation.

**Via NPM**

```sh
npm install react-query --save
```

**Via CDN**

```html
<script src="https://unpkg.com/react-query/dist/react-query.production.min.js"></script>
```

Next we will wrap our App component with **ReactQueryCache** to cache our query data. Let us add that.

**App.js**

```js
import React from 'react';
import * as Containers from './containers/index';
import { BrowserRouter as Router, Switch } from 'react-router-dom';
import { QueryCache, ReactQueryCacheProvider } from 'react-query';

import Layout from './layouts/Layout';

import 'bootstrap/dist/css/bootstrap.min.css';
import './stylesheets/styles.css';

const queryCache = new QueryCache()

const App = () => {
 return (
   <ReactQueryCacheProvider queryCache={queryCache}>
     <Router>
       <Switch>
         <Layout exact path="/" component={Containers.Home} header={true} footer={true}/>
         <Layout exact path="/about" component={Containers.About} header={true} footer={false}/>
         <Layout exact path="/profile" component={Containers.Profile} header={true} footer={true}/>
       </Switch>
     </Router>
   </ReactQueryCacheProvider>
 );
}

export default App;
```

Next let us display the public user data from github in the profile component using the useQuery hook. useQuery takes these parameters,

* A **query key** (unique key). **(required)**
* The **asynchronous function** that will resolve the data. **(required)**
* The **query options. (optional)**

The key is usually like an identifier that will be used to refetch and cache the response. On using this hook, you will receive all the destructed information that you can use in your components.

Let us look at some of the information returned from the hook that we will be using.

* **canFetchMore (Boolean)** - This value will be true if the data is paginated based on the async function, i.e if you have more 20 values and you receive the first page with 10 values then the **canFetchMore** will be true because there are 10 more values that can be fetched. After the next 10 values are fetched, it will return false.
* **data(any)** - This object will contain the resolved data from the async function that will be used to render in our components.
* **error(any)** - This object will have the error message in its message key. If status is ‘success’ or ‘loading’, error will be **null.**
* **isError(Boolean)** - if query fails, set to true else false.
* **isFetching(Boolean)** - is true until the current data is fetched. Becomes false after the fetching of data (the current page) is done.
* **isLoading -** is true until the query function is either resolved or rejected, false after that.
* **isSuccess -** becomes true when the query function is resolved and false when error is thrown.
* **status -** A text representation of isLoading, isSuccess and isError. Will contain **“success”, “loading”** or **“error”**.

In the Profile Container we will add the _useQuery_ hook as follows,

**Containers/Profile.js**

```js
import React, { Fragment } from 'react';
import UserProfile from '../../components/profile/UserProfile';
import { useQuery } from 'react-query'

const Profile = (props) => {
 const {
   data,
   error,
   isLoading
 } = useQuery('profileData', () =>
   fetch('https://api.github.com/users/SoorajSNBlaze333')
   .then(res => res.json())
 )

 if (isLoading) return <Fragment>Loading...</Fragment>

 if (error) return <Fragment><h1>Oops something went wrong</h1><p>{error.message}</p></Fragment>

 return (
   <div className="w-100 p-4">
     <UserProfile profile={data}/>
   </div>
 )
}

export default Profile;
```

If you now print the query object and run, you will see this output.

![](https://www.skcript.com/svrmedia/heroes/image2-40.png)

![](https://www.skcript.com/svrmedia/heroes/image1-4.gif)

<small>Me after thinking of all the possibilities to show the data on UI</small>

If you see that you will see 2 instances of the query objects will be automatically updated for all the states.

Now, if you click on another browser tab or visit another App and come back to this window, you will see that the query request was made again and the data was re-fetched.

![](https://www.skcript.com/svrmedia/heroes/image4-28.png)

This is because of the **isStale** property of the data. The data that is rendered will be given a query property called **stale**. The stale queries will be re fetched again when the window is focussed again or the browser is reconnected or the component is mounted. This can be toggled off using the 3 properties below,

```js
 const {
   data,
   error,
   isLoading
 } = useQuery('profileData', () =>
   fetch('https://api.github.com/users/SoorajSNBlaze333')
   .then(res => res.json())
 , {
   refetchOnWindowFocus: Boolean, //refetch when window comes to focus
   refetchOnReconnect: Boolean, //refetch when browser reconnects to server
   refetchOnMount: Boolean //refetch when component mounts
 })
```

![](https://www.skcript.com/svrmedia/heroes/image5-3.gif)

<small>Me after going back to the browser and ReactQuery gives me new data</small>

Another property is the **retry on failure**. So if a query function is rejected, the _useQuery_ hook will automatically try again 3 times, before throwing the error. You can change this using,

```js
 const {
   data,
   error,
   isLoading
 } = useQuery('profileData', () =>
   fetch('https://api.github.com/users/SoorajSNBlaze333')
   .then(res => res.json())
 , {
   retry: Boolean, //refetch if query fails
   retryDelay: Number //number of times for retry. Default is 3
 })
```

Also note that the key called **failureCount** would be incremented for each failed query. This key can be used to show that the UI tried several times to connect to the server or was unsuccessful or successful after **n** attempts and show the respective messages on UI.

So this is it for the basic set up and usage of the _useQuery_ hook. For deeper explanations you can check out the documentation [here](https://react-query.tanstack.com/docs/guides/queries). In the next article, we will be going through how to query huge data by paginating and how to use infinite queries to create an infinite scroll.