Logo

dev-resources.site

for different kinds of informations.

Building Infinite Scrolling in React Js with React Query v5

Published at
5/15/2024
Categories
webdev
typescript
react
reactquery
Author
wassim93
Author
8 person written this
wassim93
open
Building Infinite Scrolling in React Js with React Query v5

Introduction:

In the world of web development, building a todo list application is a rite of passage. In this tutorial, we'll take it a step further by integrating TypeScript for type safety and React Query for efficient data management. By the end, you'll have a fully functional todo list application with robust error handling, real-time updates, and type-safe code.

Step 1: Setting Up Your Project

To get started, let's set up a new React project with TypeScript and React Query. We'll use Vite for fast development and a modern build setup.



npm init vite@latest infinite-scroll-app --template react-ts


Enter fullscreen mode Exit fullscreen mode

after that you have to select react as our option here
framework-reactand then we will select typescript + swc (Speedy Web Compiler ) you can discover more details about it through this link https://www.dhiwise.com/post/maximize-performance-how-swc-enhances-vite-and-react
typescriptAfter finishing this you have to change directory to the project created and install dependencies



# Change directory
cd infinite-scroll-app
# install dependencies
npm install
# Install React Query
npm install react-query@latest


Enter fullscreen mode Exit fullscreen mode

Step 2: Configuring ReactQuery within our project

In order to make react query works ensure that you've wrapped your application with QueryClientProvider and provided a QueryClient instance.So your main.tsx file will look like this



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

const queryClient = new QueryClient();

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


Enter fullscreen mode Exit fullscreen mode

To Get data we will be using Fake APi JSON Placeholder with the Todo endpoint.

Step 3: Fetching data

In this step we will be fetching our data from the API and then we set the limit to 10. The expected behavior for this is to render 10 values on the screen when the page is initially loaded and then continuously add more values when the user reaches the bottom screen.
So for this purpose and to make our project organized we are going to create a folder called services and add file called api.ts inside

project_architecture

Now let's create the function to fetch our data:



export const fetchData = async (page: number) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos?_pages=${page}&_limit=${10}`
  );
  const todos = await response.json();
  return todos;
};


Enter fullscreen mode Exit fullscreen mode

Now let's go to our App.tsx file and implement the logic of implementing React infinite scrolling
Here, we will use the useInfiniteQuery hook from the React query library. Consider the block of code below:



  const { data, error, fetchNextPage, hasNextPage, isFetching, isLoading } = useInfiniteQuery({
    queryKey: ["todos"],
    queryFn: ({ pageParam }) => fetchData(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage, allPages) => {
      return lastPage.length > 0 ? allPages.length + 1 : undefined;
    },
  });


Enter fullscreen mode Exit fullscreen mode

The options for useInfiniteQuery are identical to the useQuery hook with the addition of the following:

  • initialPageParam : Required : The default page param to use when fetching the first page.

  • getNextPageParam : Required : When new data is received for this query, this function receives both the last page of the infinite list of data and the full array of all pages, as well as pageParam information.

The useInfiniteQuery hook return those properties:

  • data, which contains the data from the API.

  • error, which handles any error from fetching the data from the API.

  • fetchNextpage, which fetches the next set of data to be displayed when the user reaches the bottom.

  • hasNextpage, which checks to see if there's any more data to be displayed.

  • isFetching, which shows when the data is currently being fetched.

  • isLoading, which shows when the first set of data is being loaded from the API.

Step 4: displaying data

In this step we display the list of data that will be renered on our initial page

The data properties for useInfiniteQuery will contain 2 attributes pages and pageParams

  • data.pages: TData[] Array containing all pages.
  • data.pageParams: unknown[] Array containing all page params. so we are going to format our returned data array before displaying them


  const flatData = useMemo(() => data?.pages.flat() ?? [], [data]);

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

  if (error) return <h1>Error on fetch data...</h1>;

  return (
    <div>
      {flatData?.map((item) => (
        <div key={item.id}>
          <p>{item.title}</p>
        </div>
      ))}

      {isFetching && <div>Fetching more data...</div>}
    </div>
  );



Enter fullscreen mode Exit fullscreen mode

Step 5: Implementing scrolling behavior

To make the infinte scroll works peerfectly we have first to detect if we have reached the ned of page to trigger a new request and get new data
You might have been wondering how the DOM knows when the user scrolls to the bottom of the page. Well, that's where the IntersectionObserver API comes in! The IntersectionObserver API provides a way to detect an element's position relative to the root element or viewport.



   const observer = useRef<IntersectionObserver>();
  const lastElementRef = useCallback(
    (node: HTMLDivElement) => {
      if (isLoading) return;

      if (observer.current) observer.current.disconnect();

      observer.current = new IntersectionObserver((entries) => {
        console.log(entries);
        if (entries[0].isIntersecting && hasNextPage && !isFetching) {
          fetchNextPage();
        }
      });

      if (node) observer.current.observe(node);
    },
    [fetchNextPage, hasNextPage, isFetching, isLoading]
  );



Enter fullscreen mode Exit fullscreen mode

This piece of code observes the user's motion on the screen. If the user reaches the bottom of the screen, the IntersectionObserver is alerted and serves the next set of data. Technically, it takes note of the point where the last element in the data intersects and then if hasNextPage is true, it fetches the new set of data to display.

Step 6: Final Result

Full code for this component



import { useInfiniteQuery } from "react-query";
import { fetchData } from "./services/api";
import { useCallback, useMemo, useRef } from "react";

function App() {
  const observer = useRef<IntersectionObserver>();

  const { data, error, fetchNextPage, hasNextPage, isFetching, isLoading } = useInfiniteQuery({
    queryKey: ["todos"],
    queryFn: ({ pageParam }) => fetchData(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage, allPages) => {
      return lastPage.length > 0 ? allPages.length + 1 : undefined;
    },
  });
  const flatData = useMemo(() => data?.pages.flat() ?? [], [data]);

  const lastElementRef = useCallback(
    (node: HTMLDivElement) => {
      if (isLoading) return;

      if (observer.current) observer.current.disconnect();

      observer.current = new IntersectionObserver((entries) => {
        console.log(entries);
        if (entries[0].isIntersecting && hasNextPage && !isFetching) {
          fetchNextPage();
        }
      });

      if (node) observer.current.observe(node);
    },
    [fetchNextPage, hasNextPage, isFetching, isLoading]
  );
  if (isLoading) return <h1>Loading...</h1>;

  if (error) return <h1>Error on fetch data...</h1>;

  return (
    <div>
      {flatData?.map((item) => (
        <div key={item.id} ref={lastElementRef}>
          <p>{item.title}</p>
        </div>
      ))}

      {isFetching && <div>Fetching more data...</div>}
    </div>
  );
}

export default App;



Enter fullscreen mode Exit fullscreen mode

and this will be our result

Recording-2024-05-16-at-01-15-49 hosted at ImgBB β€” ImgBB

Image Recording-2024-05-16-at-01-15-49 hosted in ImgBB

favicon ibb.co

So that's it guys , I hope you liked of this article and i helped you to learn somthing new :D.

To access the full code for this project and explore further, you can find the repository on my: GitHub

reactquery Article's
30 articles in total
Favicon
Create an SSR Application with Vite, React, React Query and React Router
Favicon
TanStack query or RTK query.
Favicon
Simplified State Management with useRQState: A Better Alternative to useState
Favicon
Master React API Management with TanStack React Query: Best Practices & Examples
Favicon
How to Fetch Data Using Axios and React Query in ReactJS
Favicon
Efficient Refresh Token Implementation with React Query and Axios
Favicon
react-query swrjs alova In-Depth Comparison
Favicon
Mastering React Query. Simplifying Data Management in React with Separation Patterns
Favicon
Mastering React Query. Structuring Your Code for Scalability and Reusability
Favicon
Why you should try React Query?
Favicon
How to used Suspense?
Favicon
Infinite list loading πŸ€”, with React Query - useInfiniteQuery hook !
Favicon
Building a CRUD app with React Query, TypeScript, and Axios
Favicon
TLDR; Suspense in react-query
Favicon
TLDR; gcTime & staleTime in react-query
Favicon
Building Infinite Scrolling in React Js with React Query v5
Favicon
Optimized Infinite Scroll with Next.js 14 Server Actions and React Query
Favicon
Building a Todo List with TypeScript and React Query: A Comprehensive Guide
Favicon
React Safe Query - A lightweight, type-safe wrapper built around React Query
Favicon
React Query Mutations Offline React-Native
Favicon
Improving UX by using cache
Favicon
How to organize your API layer in React using React Query
Favicon
Conociendo a React Query
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Final]
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 2]
Favicon
Integrating React Query with Next.js πŸš€
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 1]
Favicon
React query for data streaming
Favicon
Manual Fetch with Tanstack Query
Favicon
Setting up React Query in your ReactΒ project

Featured ones: