如何实现React自定义分页组件的动态页码生成逻辑
问题
开发React自定义分页组件,要求:
- 每次最多显示5个页码链接
- 始终包含首页和尾页链接
- 处理总页数不足5页、当前页靠近首尾等边界场景
现有代码:
import React, { useState } from 'react'; const Pagination = ({ total, current, onPageChange }) => { const [currentPage, setCurrentPage] = useState(current); const handlePageChange = (page) => { setCurrentPage(page); onPageChange(page); }; const renderPageNumbers = () => { // Need help with the logic here }; return ( <div className="pagination"> <button onClick={() => handlePageChange(1)} disabled={currentPage === 1} > First </button> {renderPageNumbers()} <button onClick={() => handlePageChange(total)} disabled={currentPage === total} > Last </button> </div> ); }; export default Pagination;
期望renderPageNumbers能动态生成页码,比如总页数10、当前页6时,显示1 ... 4 5 6 7 8 ... 10样式的链接。
解决方案
以下是renderPageNumbers的实现逻辑,同时补充了props同步逻辑,覆盖所有边界场景:
完整代码
import React, { useState, useEffect } from 'react'; const Pagination = ({ total, current, onPageChange }) => { const [currentPage, setCurrentPage] = useState(current); // 同步外部传入的current值到内部state useEffect(() => { setCurrentPage(current); }, [current]); const handlePageChange = (page) => { setCurrentPage(page); onPageChange(page); }; const renderPageNumbers = () => { const pages = []; const maxVisiblePages = 5; // 总页数≤5时,直接渲染所有页码 if (total <= maxVisiblePages) { for (let i = 1; i <= total; i++) { pages.push( <button key={i} onClick={() => handlePageChange(i)} disabled={i === currentPage} className={i === currentPage ? 'active' : ''} > {i} </button> ); } return pages; } let startPage, endPage; // 当前页靠近首页(前3页):显示前5页 + 省略号 + 尾页 if (currentPage <= Math.ceil(maxVisiblePages / 2)) { startPage = 1; endPage = maxVisiblePages; pages.push( ...Array.from({ length: endPage - startPage + 1 }, (_, i) => { const page = startPage + i; return ( <button key={page} onClick={() => handlePageChange(page)} disabled={page === currentPage} className={page === currentPage ? 'active' : ''} > {page} </button> ); }) ); pages.push(<span key="ellipsis-end">...</span>); pages.push( <button key={total} onClick={() => handlePageChange(total)} className={total === currentPage ? 'active' : ''} > {total} </button> ); } // 当前页靠近尾页(后3页):显示首页 + 省略号 + 后5页 else if (currentPage >= total - Math.floor(maxVisiblePages / 2)) { pages.push( <button key={1} onClick={() => handlePageChange(1)} className={1 === currentPage ? 'active' : ''} > {1} </button> ); pages.push(<span key="ellipsis-start">...</span>); startPage = total - maxVisiblePages + 1; endPage = total; pages.push( ...Array.from({ length: endPage - startPage + 1 }, (_, i) => { const page = startPage + i; return ( <button key={page} onClick={() => handlePageChange(page)} disabled={page === currentPage} className={page === currentPage ? 'active' : ''} > {page} </button> ); }) ); } // 当前页在中间区域:首页 + 省略号 + 当前页前后各2页 + 省略号 + 尾页 else { pages.push( <button key={1} onClick={() => handlePageChange(1)} className={1 === currentPage ? 'active' : ''} > {1} </button> ); pages.push(<span key="ellipsis-start">...</span>); startPage = currentPage - 2; endPage = currentPage + 2; pages.push( ...Array.from({ length: endPage - startPage + 1 }, (_, i) => { const page = startPage + i; return ( <button key={page} onClick={() => handlePageChange(page)} disabled={page === currentPage} className={page === currentPage ? 'active' : ''} > {page} </button> ); }) ); pages.push(<span key="ellipsis-end">...</span>); pages.push( <button key={total} onClick={() => handlePageChange(total)} className={total === currentPage ? 'active' : ''} > {total} </button> ); } return pages; }; return ( <div className="pagination"> <button onClick={() => handlePageChange(1)} disabled={currentPage === 1} > First </button> {renderPageNumbers()} <button onClick={() => handlePageChange(total)} disabled={currentPage === total} > Last </button> </div> ); }; export default Pagination;
逻辑说明
- 总页数≤5:直接渲染所有页码,无需省略号
- 当前页靠近首页:显示前5个页码,追加省略号和尾页
- 当前页靠近尾页:显示首页、省略号,再渲染最后5个页码
- 当前页在中间:显示首页、省略号、当前页前后各2页(共5个)、省略号、尾页
- 新增
useEffect同步外部current值到内部state,确保组件状态一致 - 给激活状态的页码添加
active类,方便样式区分
内容的提问来源于stack exchange,提问作者Iljubaev Marat
相关产品推荐
相关产品推荐

