DEV Community

Cover image for #1 React Query: Introducing, Pagination, Caching and Pre-Fetching
Kevin Toshihiro Uehara
Kevin Toshihiro Uehara

Posted on

#1 React Query: Introducing, Pagination, Caching and Pre-Fetching

Hi there!!! How you doing? Everthing all right? I hope so!

Have been a long time where I don't write some article, but today I want to introduce you this amazing tool, called React query.

If you don't know or if you even listened about React Query, I will explain in these article.

React Query is a state management, but with a lot of features that you can use. For example, offline support, pre-fetching, caching, pagination, mutations, infinite scroll, auto refetching, backend agnotistic and a lot of more of features.

Documentation Page

Features of React Query

I want to create two articles to introduce you the main features that I use on my daily life and personal projects.

This first artile let's see how to configure in our project, fetch data, pagination, auto caching and pre-fetching.
We will be using Vite with React and Typescript, Tailwind for some styling.

First, let's create our project using the command:



yarn create vite react-query-demo --template react-ts


Enter fullscreen mode Exit fullscreen mode

Then, let's open the project on terminal and install the dependencies using



yarn


Enter fullscreen mode Exit fullscreen mode

or if you are using npm, just use npm install

Now, let's add the react-query dependency using:



yarn add react-query


Enter fullscreen mode Exit fullscreen mode

Also, adding the tailwind:



yarn add -D tailwindcss postcss autoprefixer


Enter fullscreen mode Exit fullscreen mode

Creating the setup files of tailwind:



npx tailwindcss init -p


Enter fullscreen mode Exit fullscreen mode

And in our tailwind.config.js created, let's add:



/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}


Enter fullscreen mode Exit fullscreen mode

And in our index.css let's clean and remove all the defaut styles and add the tailwind directives:

index.css



@tailwind base;
@tailwind components;
@tailwind utilities;


Enter fullscreen mode Exit fullscreen mode

and that's it! Now we can start our demo project!

I will clean the project, removing the App.css.

Spongebob Tired Gif

Now let's begin to code!!!

First let's clean the App.tsx and let's just leave the component function.



function App() {
  return <></>
}


Enter fullscreen mode Exit fullscreen mode

And in our main.tsx let's add the react query provider:



import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import App from "./App.tsx";

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  </React.StrictMode>
);


Enter fullscreen mode Exit fullscreen mode

The Query Client provider is the React Query Provider and we need pass the client as property. So we instantiate our client with the new Query Client().

Notice the React Query have the DevTools himself. And it's great and awesome!!!
Just add inside the provider the ReactQueryDevtools and passing the initialIsOpen property as false because every update the dev tool it will be opened.

Now you can start your application using:



yarn dev


Enter fullscreen mode Exit fullscreen mode

And let's see:

Initial Project and React Query Devtools

This flower is our dev tools of react query. We can use to analise the status of all of our requests, caching and a lot of more. (This dev tools 'flower' it's only available on development mode. In the production mode it not will be rendered).

Back to the App.tsx let's add the call of our Fake API. I will be using the json placeholder. Let's on top of the component function App(){}

We can use the pagination passing the _limit and _page as query parameter on URL.



const MAX_POST_PAGE = 5;

const fetchPosts = async (pageNumber: number) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/posts?_limit=${MAX_POST_PAGE}&_page=${pageNumber}`
  );
  const posts = (await response.json()) as Post[];
  return posts;
};


Enter fullscreen mode Exit fullscreen mode

For example:

If you access the: https://jsonplaceholder.typicode.com/posts?_limit=5&_page=1

Json Placeholder Fake API

We will have 5 posts on page 1.

Now let's add some state to represent current page itself inside of our component:



const [currentPage, setCurrentPage] = useState(1);


Enter fullscreen mode Exit fullscreen mode

And let's call the react query hook:



const { data, isError, isLoading } = useQuery(
    ["posts", currentPage],
    () => fetchPosts(currentPage),
    {
      staleTime: 0,
      keepPreviousData: true,
    }
  );


Enter fullscreen mode Exit fullscreen mode

Notice that already are using the pagination. We need to pass some unique key, called 'posts' and the current page on tuple. On the second paramenter let's call the function to fetch the posts passing the current page.

And on third parameter we can pass some options for example:
staleTime the time that data it will be available until the next update. keepPreviousData, will keep the previous data on pagination, avoiding refetch. And a lot of more properties that you can see on documentation.

Now, we will create our component:



 if (isError) return <>Error</>;

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

  return (
    <>
      <ul>
        {data?.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>

      <div className="w-screen flex justify-around items-center">
        <button
          className="border bg-blue-600 hover:bg-blue-400 text-white rounded-md w-24 h-12"
          disabled={currentPage <= 0}
          onClick={() => setCurrentPage(currentPage - 1)}
        >
          Previous
        </button>
        <p className="font-semibold">Page {currentPage}</p>
        <button
          className="border bg-blue-600 hover:bg-blue-400 text-white rounded-md w-24 h-12"
          onClick={() => setCurrentPage(currentPage + 1)}
        >
          Next
        </button>
      </div>
    </>
  );


Enter fullscreen mode Exit fullscreen mode

The useQuery function will provide a lot of status of our data fetch, like isLoading, data itself (response), isFetching, error, isError and a lot of more status that you can use.

Notice that if isLoading, I'm rendering the a element with is loading...

If has an error, I'm rendering the error message.

And if success I'm listing the data with our buttons next and previous. This buttons will increment or decrease the currentPage state.

Let's see how the application seems:

Pagination Example Working

And Woooow we have our pagination! Easy Peasy!

To Finish let's add the Pre-Fetching!

Let's add another hook called useQueryClient;



const queryClient = useQueryClient();


Enter fullscreen mode Exit fullscreen mode

And create inside of an useEffect:



useEffect(() => {
    if (currentPage < MAX_POST_PAGE) {
      const nextPage = currentPage + 1;
      queryClient.prefetchQuery([
        "posts",
        nextPage,
        () => fetchPosts(nextPage),
      ]);
    }
  }, [currentPage, queryClient]);


Enter fullscreen mode Exit fullscreen mode

The queryClient will be pre-fetching the next data of our page. So we improved the user experience of our application, getting the next page and the loader will not be rendered, because always we have the next page.

And showing the entire component:



import { useEffect, useState } from "react";
import { Post } from "./types";
import { useQuery, useQueryClient } from "react-query";

const MAX_POST_PAGE = 5;

const fetchPosts = async (pageNumber: number) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/posts?_limit=${MAX_POST_PAGE}&_page=${pageNumber}`
  );
  const posts = (await response.json()) as Post[];
  return posts;
};

function App() {
  const [currentPage, setCurrentPage] = useState(1);

  // pre-fetching
  const queryClient = useQueryClient();

  useEffect(() => {
    if (currentPage < MAX_POST_PAGE) {
      const nextPage = currentPage + 1;
      queryClient.prefetchQuery([
        "posts",
        nextPage,
        () => fetchPosts(nextPage),
      ]);
    }
  }, [currentPage, queryClient]);

  const { data, isError, isLoading } = useQuery(
    ["posts", currentPage],
    () => fetchPosts(currentPage),
    {
      staleTime: 0,
      keepPreviousData: true,
    }
  );

  if (isError) return <>Error</>;

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

  return (
    <>
      <ul>
        {data?.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>

      <div className="w-screen flex justify-around items-center">
        <button
          className="border bg-blue-600 hover:bg-blue-400 text-white rounded-md w-24 h-12"
          disabled={currentPage <= 0}
          onClick={() => setCurrentPage(currentPage - 1)}
        >
          Previous
        </button>
        <p className="font-semibold">Page {currentPage}</p>
        <button
          className="border bg-blue-600 hover:bg-blue-400 text-white rounded-md w-24 h-12"
          onClick={() => setCurrentPage(currentPage + 1)}
        >
          Next
        </button>
      </div>
    </>
  );
}

export default App;


Enter fullscreen mode Exit fullscreen mode

And the final result of our application:

Pre-Fetching

Amazing, isnt'it? Easy Peasy to use, and the React Query will provide amazing features.
In the next article I will explain the Infine Scroll, for example, any social medias use as feature. Is some pagination, but with scrolling, without buttons.

So I see you on the second article of React Query! I see you soon!

I hope you liked of this content!
Thank you so much, folks!

My Social Medias:

Linkedin: https://www.linkedin.com/in/kevin-uehara/
Instagram: https://www.instagram.com/uehara_kevin/
Twitter: https://twitter.com/ueharaDev
Github: https://github.com/kevinuehara
dev.to: https://dev.to/kevin-uehara
YouTube: https://www.youtube.com/@ueharakevin/

Thank you, the office gif

Top comments (2)

Collapse
 
filimon4 profile image
Filmon

That's great article. Thanks you very much!!. useInfiniteQuery doesn't work well, lol

Collapse
 
sanket999999999 profile image
sanket999999999

wow that's awesome