DEV Community

Cover image for Beginner's Guide to React Query
Hafiz Muhammad Bilal
Hafiz Muhammad Bilal

Posted on

Beginner's Guide to React Query

Data fetching is an essential part of building modern web applications. As there is no specific approach to fetch data from API in React, developers do it in their own ways. One of the most popular method is to use React's hook useEffect.

Disadvantages of existing data fetching techniques (useEffect)

One of the biggest disadvantages of using the useEffect hook for data fetching is the unnecessary re-renders that can occur. This can lead to poor performance, especially in complex applications with multiple components and large data sets. Additionally, managing state and handling errors can become challenging when using useEffect.

Intro to React Query

React Query is a library that makes it easy to fetch, cache, and manage data in your React applications. It provides a simple and effective solution for data fetching that overcomes many of the disadvantages of using useEffect.

Implementation

Enough talk, let's code now.

Creating React App

Let's create a react app first.

npx create-react-app react-query-guide
cd react-query-guide
npm start
Enter fullscreen mode Exit fullscreen mode

or

yarn create react-app react-query-guide
cd react-query-guide
yarn start
Enter fullscreen mode Exit fullscreen mode

Your app would be live on http://localhost:3000

Install react-query

npm install react-query
Enter fullscreen mode Exit fullscreen mode

or

yarn add react-query
Enter fullscreen mode Exit fullscreen mode

Setup JSON Server

Now let's setup a json server for users API
Install json-server package:

npm install json-server
Enter fullscreen mode Exit fullscreen mode

or

yarn add json-server
Enter fullscreen mode Exit fullscreen mode

Create a file db.json and populate following data in it:

{
  "users": [
    {
      "id": 1,
      "name": "Bilal Riaz"
    },
    {
      "id": 2,
      "name": "Haris Ejaz"
    },
    {
      "id": 3,
      "name": "Hamza Hameed"
    },
    {
      "id": 4,
      "name": "Zeeshan Ali"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Add a script in package.json file as:

"serve-json": "json-server --watch db.json --port 4000"
Enter fullscreen mode Exit fullscreen mode

Then execute

npm run serve-json
Enter fullscreen mode Exit fullscreen mode

or

yarn run serve-json
Enter fullscreen mode Exit fullscreen mode

Your server would be running on http://localhost:4000

Introducing React Query

Now it's time to use React Query in our app. Let's update our App.js file:

import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient();

const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <div>App</div>
    </QueryClientProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

We have created a queryClient with the use of QueryClient, provide it to QueryClientProvider and wrap the whole app with it. This is essential to make use of React Query's features throughout the app.

Fetching Data with useQuery

Now it's time to make network requests. Let's install axios for this purpose.
Install axios:

npm install axios
Enter fullscreen mode Exit fullscreen mode

or

yarn add axios
Enter fullscreen mode Exit fullscreen mode

Create a component Users.js to display fetched users from API.

import axios from 'axios';
import { useQuery } from 'react-query';

const fetchUsers = () => axios.get('http://localhost:4000/users');

const Users = () => {
  const { isLoading, data } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
  });

  if (isLoading) {
    return <h2>Loading...</h2>;
  }

  return (
    <div>
      {data?.data?.map((user) => (
        <p key={user?.id}>{user?.name}</p>
      ))}
    </div>
  );
};

export default Users;
Enter fullscreen mode Exit fullscreen mode

Here we're using useQuery hook to fetch users data. We have provided a unique queryKey and a function fetchUsers which returns a promise to queryFn property.

We get several properties from useQuery hook as two of them isLoading & data have been shown in the code.

Then make use of Users.js component in App.js:

import { QueryClient, QueryClientProvider } from 'react-query';
import Users from './Users';

const queryClient = new QueryClient();

const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <Users />
    </QueryClientProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

We have successfully fetched users with the use of React Query.

cacheTime

By default, your query remains in the cache for 5 minutes but you can configure it as:

const { isLoading, data } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
    cacheTime: 2000
  });
Enter fullscreen mode Exit fullscreen mode

You can configure cacheTime in milliseconds.
You are probably thinking what actually cacheTime means? Basically it is the time for how long your query would be cached. For example, you fetch users with queryKey: "users", fetching users would bring data from cache and fetching from API continues in the background and then updates the cached data with the data from API.

staleTime

By default, every query will trigger a network request if you are fetching something from API. But you can use the cached data instead of making network request again & again. It is helpful in the case where we know that the data coming from API is static as it doesn’t often change. To do so, you can configure staleTime in milliseconds as:

const { isLoading, data } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
    staleTime: 30000  //0ms by default
  }
Enter fullscreen mode Exit fullscreen mode

Fetching Data on Button Click

Till now, API request is being made when component mounts. If we want to fetch users on some button click, we would configure useQuery hook as:

const { isLoading, data, refetch } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
    enabled: false
  }
Enter fullscreen mode Exit fullscreen mode

refetch is a function to run the queryFn function.
We would add a button in jsx and configure it to fetch users on clicking it.

    <div>
      <button onClick={refetch}>Fetch Users</button>
      {data?.data?.map((user) => (
        <p key={user?.id}>{user?.name}</p>
      ))}
    </div>
Enter fullscreen mode Exit fullscreen mode

Posting Data with useMutation

Enough for fetching data, it's time to talk about posting/updating data. We would use useMutation hook for this purpose. Let's implement adding user feature with it.
Update your Users.js file as:

import axios from 'axios';
import { useMutation, useQuery } from 'react-query';

const fetchUsers = () => axios.get('http://localhost:4000/users');

const addNewUser = () =>
  axios.post('http://localhost:4000/users', {
    name: 'Ali Zuberi',
  });

const Users = () => {
  const { isLoading, data, refetch } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
  });
  const { mutate } = useMutation(addNewUser);

  if (isLoading) {
    return <h2>Loading...</h2>;
  }

  return (
    <div>
      <button onClick={refetch}>Fetch Users</button>
      <button onClick={mutate}>Add User</button>
      {data?.data?.map((user) => (
        <p key={user?.id}>{user?.name}</p>
      ))}
    </div>
  );
};

export default Users;

Enter fullscreen mode Exit fullscreen mode

We have created addNewUser function which return a promise and assign it to the useMutation hook. We have extracted out mutate function from the hook which would be called on clicking Add User button.

Refetch Data After Mutation

New user can be added successfully till now. But the issue is that adding a new user is not re-fetching the updated users list. To do so, configure useMutation with onSuccess function as:

  const { mutate } = useMutation(addNewUser, {
    onSuccess: refetch,
  });
Enter fullscreen mode Exit fullscreen mode

Mutate Data with Dynamic Values

If you want to add a new user with the name provided by an input, update Users.js file as:

import axios from 'axios';
import { useState } from 'react';
import { useMutation, useQuery } from 'react-query';

const fetchUsers = () => axios.get('http://localhost:4000/users');

const addNewUser = (userName) =>
  axios.post('http://localhost:4000/users', {
    name: userName,
  });

const Users = () => {
  const [username, setUsername] = useState('');
  const { isLoading, data, refetch } = useQuery({
    queryKey: 'users',
    queryFn: fetchUsers,
  });
  const { mutate } = useMutation(addNewUser, {
    onSuccess: refetch,
  });

  if (isLoading) {
    return <h2>Loading...</h2>;
  }

  return (
    <div>
      <div>
        <input
          placeholder='Enter name'
          value={username}
          onChange={(e) => setUsername(e?.target?.value)}
        />
      </div>
      <button onClick={() => mutate(username)}>Add User</button>
      <button onClick={refetch}>Fetch Users</button>
      {data?.data?.map((user) => (
        <p key={user?.id}>{user?.name}</p>
      ))}
    </div>
  );
};

export default Users;

Enter fullscreen mode Exit fullscreen mode

Conclusion

React Query is a powerful tool for simplifying data management in React applications, making it easier to fetch, cache, and manage data with ease. Whether you're a beginner or an experienced developer, incorporating React Query into your workflow can greatly enhance the performance and efficiency of your applications. So, start exploring its capabilities today and elevate your React development game.

Links

Here is the Github repository for the code covered in this blog.
Co-authored by - harisbinejaz

Top comments (0)