With the number of articles both in my portfolio and blogging platform slowly increasing, adding pagination had become increasingly necessary. For the portfolio, pagination improves the viewing comfort for visitors. On the blogging platform, pagination was essential because the layout was not designed to handle more than ten articles at once. In this article, I’ll show you how to implement pagination without requiring changes to the backend.
Create a New Pagination Component
The first step is to create a pagination component that is separate from the parent component. This separation ensures:
- Reusability: The component can be reused in other parts of the application or even other projects.
- Scalability: Features like a dropdown to select the number of items per page can be added later.
Here’s the code for the Pagination component:
import React from "react";
interface PaginationProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
}
const Pagination: React.FC<PaginationProps> = ({ currentPage, totalPages, onPageChange }) => {
const handleClick = (page: number) => {
if (page > 0 && page <= totalPages) {
onPageChange(page);
}
};
return (
<div>
<button
onClick={() => handleClick(currentPage - 1)}
disabled={currentPage === 1}
>
Previous
</button>
{Array.from({ length: totalPages }, (_, index) => (
<button
key={index + 1}
onClick={() => handleClick(index + 1)}
disabled={currentPage === index + 1}
>
{index + 1}
</button>
))}
<button
onClick={() => handleClick(currentPage + 1)}
disabled={currentPage === totalPages}
>
Next
</button>
</div>
);
};
export default Pagination;
This component renders three types of buttons:
- Previous Page Button: Disabled if the user is on the first page.
- Page Number Buttons: Dynamically generated based on the total number of pages.
- Next Page Button: Disabled if the user is on the last page.
When a user clicks on any of these buttons, the onPageChange event is triggered and updates the page state in the parent component.
Modify the Parent Component
The parent component handles API calls and manages pagination states. Here are the key steps:
Create State Variables:
currentPage to track the current page.
totalPages to calculate the total number of pages based on the fetched data.
Handle Page Changes:
Update currentPage when a user interacts with the Pagination component.
Dynamically slice the fetched data to display items for the selected page.
Here’s a simplified version of the parent component:
import { useEffect, useState } from "react";
import Container from "react-bootstrap/Container";
import BlogCard from "./BlogCard";
import Pagination from "../../components/Pagination";
import axios from "axios";
interface BlogPost {
id: number;
title: string;
author: string;
thumbnail: string;
content: string;
publishedAt: string;
}
const apiUrl = import.meta.env.VITE_API_URL;
export default function BlogList() {
const [blogPosts, setBlogPosts] = useState<BlogPost[]>([]);
const [currentPage, setCurrentPage] = useState<number>(1);
const [totalPages, setTotalPages] = useState<number>(0);
const itemsPerPage = 10;
useEffect(() => {
const fetchBlogPosts = async () => {
try {
const response = await axios.get(`${apiUrl}/api/blog`);
setBlogPosts(response.data);
setTotalPages(Math.ceil(response.data.length / itemsPerPage));
} catch (error) {
console.error("Error fetching blog posts", error);
}
};
fetchBlogPosts();
}, []);
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const startIndex = (currentPage - 1) * itemsPerPage;
const currentItems = blogPosts.slice(startIndex, startIndex + itemsPerPage);
return (
<Container>
<h2>Welcome to the blog</h2>
{currentItems.map((blog) => (
<BlogCard
key={blog.id}
id={blog.id}
title={blog.title}
thumbnail={blog.thumbnail}
excerpt={blog.content.substring(0, 60) + "..."}
/>
))}
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
/>
</Container>
);
}
Conclusion
With this setup, you now have a scalable and reusable pagination component. It works seamlessly with the parent component and dynamically adjusts as the number of posts increases. This approach requires no backend changes, making it ideal for projects where the backend does not support pagination out of the box.
You can further enhance the component by adding features like a dropdown to select the number of items per page or search and filter options. For now, this implementation provides a solid foundation for handling pagination in React with TypeScript.
Top comments (0)