#1 React Query: Introducing, Pagination, Caching and Pre-Fetching
Tags: react, typescript, javascript, webdev
Published: 2024-01-14 20:15:00

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dciw7ab0wm7yw4b2tmg7.png)


![Features of React Query](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n489jowagwu8rpenu9r0.png)

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:

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

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

```
yarn
```

or if you are using npm, just use `npm install`

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

```sh
yarn add react-query
```

Also, adding the tailwind:

```sh
yarn add -D tailwindcss postcss autoprefixer
```

Creating the setup files of tailwind:

```sh
npx tailwindcss init -p
```

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

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

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

`index.css`
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

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

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

![Spongebob Tired Gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2ehdp8wqwehyhq6cha3.gif)

Now let's begin to code!!!

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

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

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

```tsx
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>
);
```

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:

```sh
yarn dev
```

And let's see:

![Initial Project and React Query Devtools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gko8dux7kumqkpjtx8a8.gif)

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.

```tsx
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;
};
```

For example:

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

![Json Placeholder Fake API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2hou1bpmk9xnlr3bxgj.png)

We will have 5 posts on page 1.

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

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

And let's call the react query hook:

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

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:

```tsx
 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>
    </>
  );
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qi6j9ilat1y9xignox1a.gif)

And Woooow we have our pagination! Easy Peasy!

To Finish let's add the `Pre-Fetching`!

Let's add another hook called `useQueryClient`;

```tsx
const queryClient = useQueryClient();
```

And create inside of an useEffect:

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

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:

```tsx
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;
```

And the final result of our application:

![Pre-Fetching](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cvjp7x01kysk0sfdc1l2.gif)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6l9t939ma7a4aj0h12q.gif)