How to auto-pull data in React-Query

Introduction

React-Query is a powerful tool for fetching/caching the data on the frontend side, yet working with React-Query might be a bit not straightforward, in this article we’re going to see how we can implement a functionality to auto-pull or refetch the data each X time from the backend.

Let’s get started!

auto-pull or refetching or real-time fetching

To achieve auto-pulling or refetching or real-time fetching data from the backend using React-Query, we’re going to implement a tiny Todo app, in which the user can enter his to-do list, then submit the to-do item to the server. At the same time, we will add the functionality to pull the data from the backend each X time. By this, we will be able to achieve the goal of implementing auto-pull data using React-Query.

Full example source code

For this article, we will use the NextJS starter kit by stackBlitz, and we will implement a small endpoint that simulates adding/retrieving the to-do list items.

Backend Side

The following code implementation is sitting under src/pages/api/data.js file.

// A simple endpoint for getting current todo list
let list = ['Item 1', 'Item 2', 'Item 3'];

export default async (req, res) => {
  if (req.query.add) {
    if (!list.includes(req.query.add)) {
      list.push(req.query.add);
    }
  } else if (req.query.clear) {
    list = [];
  }

  await new Promise((r) => setTimeout(r, 500));

  res.json(list);
}

We have a predefined list of to-do items on line 2.

Then, we’ve two statement checks, we will rely on the first one if (req.query.add) to add new to-do items,

and on the 2nd one if (req.query.clear) to clear the to-do list items and return an empty list.

In line 12, we’re adding a time delay of 500ms to simulate that the request is taking some time in processing.


Frontend Side

On the front end side, we need to create a single file src/pages/index.js that will have the implementation of our React-Query component and the wrapper component.

First, let’s create our wrapper component <App> inside the file:

import React from 'react';
import axios from 'axios';
import {
  useQuery,
  useQueryClient,
  useMutation,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query';

const queryClient = new QueryClient();

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  );
}

As shown above, we’ve added imports for React, Axios, and some other React-Query functionalities.

Then we’ve initialized a new QueryClient const queryClient = new QueryClient();, we will use this to pass it as a prop to the QueryClientProvider as shown in line 15.


Now, let’s add the implementation for the <Example> component:

function Example() {
  const queryClient = useQueryClient();
  const [value, setValue] = React.useState('');

  const { status, data, error } = useQuery(
    ['todos'],
    async () => {
      const res = await axios.get('/api/data');
      return res.data;
    },
    {
      // Refetch the data every 1 second = 1000 ms
      refetchInterval: 1000,
    }
  );

  const addMutation = useMutation((value) => fetch(`/api/data?add=${value}`), {
    onSuccess: () => queryClient.invalidateQueries(['todos']),
  });

  const clearMutation = useMutation(() => fetch(`/api/data?clear=1`), {
    onSuccess: () => queryClient.invalidateQueries(['todos']),
  });

  if (status === 'loading') return <h1>Loading...</h1>;
  if (status === 'error') return <span>Error: {error.message}</span>;

  return (
    <div>
      <h1>Auto Refetch/Auto Pulling</h1>
      <h2>Todo List</h2>
      <form
        onSubmit={(event) => {
          event.preventDefault();
          addMutation.mutate(value, {
            onSuccess: () => {
              setValue('');
            },
          });
        }}
      >
        <input
          placeholder="Enter item"
          value={value}
          onChange={(ev) => setValue(ev.target.value)}
        />
        <div style={{ marginTop: '10px', width: '150px' }}>
          <button type="submit" style={{ width: '100%' }}>
            Add
          </button>
        </div>
      </form>
      <ul>
        {data.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      <div>
        <button
          onClick={() => {
            clearMutation.mutate();
          }}
        >
          Clear All
        </button>
      </div>
    </div>
  );
}

Fetch Query (auto-pull)

At line2, we’ve created a new instance on queryClient, and this will be used to call the invalidateQueries method over the todos query, once the user added a new to-do item or once he cleared the list of the items.

In line3, we’ve created a new state variable value which will hold the new to-do item the user entered in the input field. Then we will use this value in submitting the form (line35).

In line5, we’ve added the implementation of the todos query, which has the following properties:

  • queryKey ['todos'].
  • the async function that uses axios to call the backend endpoint api/data to fetch the to-do list.
  • refetchInterval which is set to 1000ms for now, and this value will tell React-Query to auto-pull the data (i.e: execute the query) every 1000ms.

Submit/Mutate Queries

In line17, we’ve hooked up useMutation with the `/api/data?add=${value}` backend API and we will use the mutate function in order to submit the data to the backend.

Once the submit request succeeded (onSuccess), we will invalidate the query (a new request will be sent to the backend to fetch the latest list of the to-do items).

queryClient.invalidateQueries(['todos'])

In line21, we’ve used the useMutation hook in the same way as we did in line17, the only difference here is that we’re using this request to clear the data from the backend. And on the request success response, we do invalidate the query again.


Component render implementation

Inside the component render method, we’ve created a simple form with one input field and two buttons. A submit button to submit the form, and a clear button to clear the to-do list items.


Testing the implementation

Now try to open the app in the browser, you should be able to see the following page:

React-Query auto-pull example
React-Query auto-pull example

Trying to add a new to-do item should work fine without any issues, as shown below.

React-Query auto-pull example
React-Query auto-pull example

That’s awesome! But how we can make sure that the auto-pulling or refetching is working as expected?

A simple way to do that, we can open the link in two different tabs, and try to submit the data from one tab, you should be able to see it gets updated in the 2nd tab after 1000ms, also when trying to clear the list in one tab, we also should be able to see the list clear in the 2nd tab as well after 1000ms. Let’s check that out.

React-Query auto-pull example
React-Query auto-pull example

that’s it for How to auto-pull data in React-Query, And as always happy coding!

Full example source code

Photo from unsplash

Related Posts

How to Capture Screenshots with Puppeteer In NodeJS

How to Capture Screenshots with Puppeteer In NodeJS

To Capture Screenshots with Puppeteer: Launch a Browser Instance Navigate to the Web Page Capture the Screenshot Introduction: Puppeteer is a powerful Node.js library that allows developers…

How to Minimize Puppeteer Browser Window To Tray

How to Minimize Puppeteer Browser Window To Tray

Puppeteer is a powerful tool for automating tasks in headless or non-headless web browsers using JavaScript. While Puppeteer is often used to perform actions within a browser,…

Intercepting Responses in Node.js with Puppeteer

Intercepting Responses in Node.js with Puppeteer

Introduction: Puppeteer is a powerful Node.js library that provides a high-level API for controlling headless Chrome or Chromium browsers. It’s widely used for web scraping, automated testing,…

Mastering React Component Re-rendering in Jest

Mastering React Component Re-rendering in Jest

In this hands-on guide, we’ll explore the art of optimizing React component re-rendering within Jest tests. By combining theory with practical coding examples, you’ll gain a deep…

Eliminating Nesting Loops in React Rendering

Eliminating Nesting Loops in React Rendering

React has ushered in a new era of web application development with its component-based structure, promoting code reusability and maintainability. But as projects evolve, achieving optimal performance…

Exploring Type and Interface Usage in TypeScript

Exploring Type and Interface Usage in TypeScript

TypeScript has gained immense popularity by bridging the gap between dynamic JavaScript and static typing. Two of its fundamental features, “Type” and “Interface,” play pivotal roles in…

Leave a Reply

%d bloggers like this: