You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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;

逻辑说明

  1. 总页数≤5:直接渲染所有页码,无需省略号
  2. 当前页靠近首页:显示前5个页码,追加省略号和尾页
  3. 当前页靠近尾页:显示首页、省略号,再渲染最后5个页码
  4. 当前页在中间:显示首页、省略号、当前页前后各2页(共5个)、省略号、尾页
  5. 新增useEffect同步外部current值到内部state,确保组件状态一致
  6. 给激活状态的页码添加active类,方便样式区分

内容的提问来源于stack exchange,提问作者Iljubaev Marat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:44:50