Open In App

Implementing Pagination and Infinite Scrolling with React Hooks

Last Updated : 12 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This article will discuss pagination and infinite scrolling. These concepts are commonly used in web applications to enhance the user experience. Pagination and infinite scrolling both help in the efficient handling of huge data sizes.

Approach to Implement Pagination and Infinite Scrolling:

Pagination:

  • Three state variables to maintain – page, numberOfPages, data
    • const [page, setPage] = useState(1) to manage current page
    • const [numberOfPages, setNumberOfPages] = useState(0) – to manage total number of pages
    • const [data, setData] = useState([]) – to manage the data for the current page
  • Get data from the data file or api inside useEffect() hook using callback function
    • Add page and numberOfPages as dependency in useEffect() hook
  • onClick() function on next and previous button to move to different pages
    • Previous button should be disabled when user is on first page
    • Next button should disabled when user is on last page
  • Data received from file or api are to be mapped to specific UI Element
  • Add CSS styles

Infinite Scrolling:

  • Four state variables to maintain – loadedData, page, moreData, loading
    • const [loadedData, setLoadedData] = useState([]) – to manage already loaded data
    • const [page, setPage] = useState(1) – to manage current page
    • const [moreData, setMoreData] = useState(true) – to check if response has more data or not
    • const [loading, setLoading] = useState(false) – to track whether data has fully loaded or not
  • useEffect() used for two purpose –
    • to set values of state variables
    • to add event listener to the scroll event
  • Fetch more data when user scrolls to the end of the page
  • Map the fetched data to specific UI Elements
  • Add CSS Styles

Steps to Create the React App:

Step 1: Create new react app

npx create-react-app pagination-and-infinite-scrolling

Step 2: Change the directory

cd pagination-and-infinite-scrolling

Step 3: Install the required dependencies

npm install <package-name>

Step 4: Create components folder and utils folder inside src directory

cd src
mkdir components
mkdir utils

Step 5: Create Pagination.jsx and InfiniteScrolling.jsx inside the components folder

Project Structure:

Screenshot-2024-03-08-174733

OUTPUT IMAGE FOR PROJECT STRUCTURE

The updated dependencies in package.json file will look like:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Below is the code example for Pagination:

CSS
.container {
    display: flex;
    flex-direction: column;
    justify-content: left;
    width: 30%;
    color: darkslategrey;
    border: 1px solid black;
}

.list-item {
    border-bottom: 1px solid black;
}

.list-item-span1 {
    display: inline-flex;
    flex-direction: column;
    justify-content: space-evenly;
    border-right: 1px solid black;
    width: 50%;
}

.list-item-span2 {
    padding-bottom: 0.07rem;
    width: 50%;
}
Javascript
// App.js

import React from 'react';
import Pagination
    from './components/Pagination';
import './App.css';

function App() {
    return (
        <div>
            <h1>Pagination</h1>
            <Pagination />
        </div>
    );
}

export default App;
Javascript
// Pagination.jsx

import React,
{
    useState,
    useEffect
} from 'react';
import GetData from '../utils/GetData';

function Pagination() {
    const [page, setPage] = useState(1);
    const [numberOfPages, setNumberOfPages] = useState(0);
    const [data, setData] = useState([]);

    useEffect(() => {
        GetData(page)
            .then(response => {
                setData(response.data);
                setNumberOfPages(response.totalPages);
            });
    }, [page, numberOfPages]);
    const nextPage = () => {
        setPage(pageNo => pageNo + 1);
    };

    const prevPage = () => {
        setPage(pageNo => pageNo - 1);
    };

    return (
        <div className='container'>
            <div className='list-item'
                style={
                    {
                        fontWeight: 'bold',
                        backgroundColor: 'lightgray'
                    }}>
                <span className='list-item-span1'>
                    Item Name
                </span>
                <span className='list-item-span2'>
                    Item ID
                </span>
            </div>
            {
                data && data.map(item => (
                    <div key={item.id} className='list-item'>
                        <span className='list-item-span1'>
                            {item.name}
                        </span>
                        <span className='list-item-span2'>
                            {item.id}
                        </span>
                    </div>
                ))
            }
            <button onClick={prevPage}
                disabled={page === 1}>
                Prev
            </button>
            <button onClick={nextPage}
                disabled={page === numberOfPages}>
                Next
            </button>
        </div>
    );
}

export default Pagination;
Javascript
// GetData.js

import data from '../data';

const GetData = async (page) => {
    const pageSize = 10;
    const startIndex =
        (page - 1) * pageSize;
    const endIndex =
        startIndex + pageSize;
    const slicedData =
        data.slice(startIndex, endIndex);

    return new Promise(resolve => {
        setTimeout(() => {
            resolve(
                {
                    data: slicedData,
                    totalPages: Math.ceil(data.length / pageSize)
                });
        }, 1000);
    });
};

export default GetData;
Javascript
const data = [
    { id: 1, name: "Item 1" },
    { id: 2, name: "Item 2" },
    { id: 3, name: "Item 3" },
    { id: 4, name: "Item 4" },
    { id: 5, name: "Item 5" },
    { id: 6, name: "Item 6" },
    { id: 7, name: "Item 7" },
    { id: 8, name: "Item 8" },
    { id: 9, name: "Item 9" },
    { id: 10, name: "Item 10" },
    { id: 11, name: "Item 11" },
    { id: 12, name: "Item 12" },
    { id: 13, name: "Item 13" },
    { id: 14, name: "Item 14" },
    { id: 15, name: "Item 15" },
    { id: 16, name: "Item 16" },
    { id: 17, name: "Item 17" },
    { id: 18, name: "Item 18" },
    { id: 19, name: "Item 19" },
    { id: 20, name: "Item 20" },
    { id: 21, name: "Item 21" },
    { id: 22, name: "Item 22" },
    { id: 23, name: "Item 23" },
    { id: 24, name: "Item 24" },
    { id: 25, name: "Item 25" },
    { id: 26, name: "Item 26" },
    { id: 27, name: "Item 27" },
    { id: 28, name: "Item 28" },
    { id: 29, name: "Item 29" },
    { id: 30, name: "Item 30" }
];

export default data;  

Steps to run the App:

npm start

Output:

outputPaginationGif

OUTPUT GIF FOR PAGINATION

Example: Below is the code example for Infinite Scrolling:

CSS
.infiniteScroll {
    border: 1px solid black;
}
Javascript
// App.js
import React from 'react';
import InfiniteScroll
    from './components/InfiniteScrolling';
import './App.css';

function App() {
    return (
        <div>
            <h1>Infinite Scroll</h1>
            <InfiniteScroll />
        </div>
    );
}

export default App;
Javascript
// InfiniteScroll.js

import React,
{
    useState,
    useEffect
} from 'react';
import GetData from '../utils/GetData';

function InfiniteScroll() {
    const [loadedData, setLoadedData] = useState([]);
    const [page, setPage] = useState(1);
    const [loading, setLoading] = useState(false);
    const pageSize = 10;
    const [moreData, setMoreData] = useState(true);

    useEffect(() => {
        const loadMoreData = async () => {
            try {
                setLoading(true);
                const newData = await GetData(page, 2);
                if (Array.isArray(newData)
                    && newData.length > 0) {
                    setLoadedData(
                        prevData =>
                            [...prevData, ...newData]
                    );
                    setPage(prevPage => prevPage + 1);
                }
                else {
                    setMoreData(false);
                }
            }
            catch (error) {
                console.error('Error fetching data:', error);
                setMoreData(false);
            } finally {
                setLoading(false);
            }
        };

        if (moreData && !loading) {
            loadMoreData();
        }
    }, [page, pageSize, moreData, loading]);

    const handleScroll = () => {
        if (
            window.innerHeight +
            document.documentElement.scrollTop ===
            document.documentElement.offsetHeight
        ) {
            setPage(prevPage => prevPage + 1);
        }
    };

    useEffect(() => {
        window.addEventListener('scroll', handleScroll);
        return () =>
            window.removeEventListener('scroll', handleScroll);
    }, []);

    return (
        <div>
            {loadedData.map(item => (
                <>
                    <div key={item.id} className='infiniteScroll'>
                        <h3>ID : {item.id}</h3>
                        <hr />
                        <h4>Title : {item.title}</h4>
                        <p>Body : {item.body}</p>
                    </div>
                    <br />
                </>
            ))}
            {
                loading &&
                <div>Loading...</div>
            }
            {
                !loading &&
                !moreData &&
                <div>
                    No more data
                </div>
            }
        </div>
    );
}

export default InfiniteScroll;
Javascript
// GetData.js

const GetData = async (page, pageSize) => {
    const response =
        await fetch(
`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${pageSize}`);
    const data =
        await response.json();
    return data;
};

export default GetData;

Steps to run the App:

npm start

Output:

outputInfiniteScrollGif

OUTPUT GIF FOR INFINITE SCROLLING



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads