在你的 React 中构建此分页

2025-06-07

在你的 React 中构建此分页

我从 API 获取了一些数据,并希望对其进行分页。在本文中,我将使用 React 实现分页。

要求

假设 API 返回的是页面总值,我需要用这个值进行分页。同时,我还需要保留当前页面,以防用户跳转到其他页面。

这是要求:

简单要求

流动

现在,我们有了一个简单的需求。在进行编码步骤之前,我先画出流程图。

在这个例子中,我认为我需要:

  1. 根据页面总值呈现所有页面。
  2. pageIndex指向当前页面的状态。

编码

首先,我们需要有一个 Next.js 项目。我们赶紧行动起来吧!

npx create-next-app@latest --typescript
Enter fullscreen mode Exit fullscreen mode

步骤1:使用总页面值渲染所有页面。

仅用于演示,所以我将硬编码页面总值。我将其设置为 20。

import type {NextPage} from 'next'
import styles from '../styles/Home.module.css'

const PAGE_TOTAL = 20

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <ul className={styles.pagination}>
        {Array.from({length: PAGE_TOTAL}, (_, idx) => (
          <li className={styles.pageItem}>{idx + 1}</li>
        ))}
      </ul>
    </div>
  )
}

export default Home
Enter fullscreen mode Exit fullscreen mode

注意我如何呈现页面,并且页面编号仅使用Array.fromidx + 1

步骤2:制作当前页面。

这一步我需要制作当前页面。

首先,在 React 中定义一个新的状态:

const [pageIndex, setPageIndex] = React.useState(0)
Enter fullscreen mode Exit fullscreen mode

默认情况下,当前页为 0。

接下来,添加一个帮助我们更改当前页面的功能。

function handlePageChange(idx: number) {
  setPageIndex(idx)
}
Enter fullscreen mode Exit fullscreen mode

最后,添加当前页面的样式和onClick事件。

<li
  className={`${styles.pageItem} ${
    idx === pageIndex ? styles.currentPage : ''
  }`}
  onClick={() => handlePageChange(idx)}
>
  {idx + 1}
</li>
Enter fullscreen mode Exit fullscreen mode

现在,我们可以更改当前页面。

页面切换

到目前为止,我们几乎完成了分页。但假设我只想在屏幕上渲染 5 个页面,并且每当我点击另一个页面时,页面数就会自动增加。

我可以做到嗎?

是的,我们前进吧!

下一个要求

在本节中,我们将使分页更具交互性。我们需要在屏幕上渲染 5 个页面,其他页面会根据当前页面自动更改。

为了便于想象,请看下面的图片:

新要求

下一个流程

根据上述要求,为了实现新的分页,我需要为分页创建一个新的流程。

我们有三种情况:

  1. 总页面值小于5。
  2. 总页面价值大于5加上pageIndex价值。
  3. 总页面价值小于5加上pageIndex价值。

让我们更详细地分析一下上述这些案例!

总页面值小于5。

我把总页数设置为 20,这肯定不会发生。但在现实世界中,也许会发生。

这样的话,我们不需要再修改逻辑代码了,只需要保留之前需求中的代码即可。

总页面价值大于5加上pageIndex价值。

每当我们点击新页面时,被点击的页面将移动到第一个位置,其余页面将自动呈现。

第二种情况

总页面价值小于5加上pageIndex价值。

这种情况下,我们不能自动增加页面数,只能改变当前页面数的值。

第三个案例

下一步编码

我们需要对之前的代码进行三处修改:

  • 页码。
  • 总页数显示在屏幕上。
  • 我们用来改变当前页面的函数。
  • 当前页面逻辑。

总页面值小于5。

<li
  className={`${styles.pageItem} ${
    idx === pageIndex ? styles.currentPage : ''
  }`}
  onClick={() => handlePageChange(idx)}
>
  {idx + 1}
</li>
Enter fullscreen mode Exit fullscreen mode

总页面价值大于5加上pageIndex价值。

在这种情况下,我们需要实现以下这些要求:

  • 页码是当前页面索引加上我们点击的当前索引。
  • 总页数:5。
  • 当前页面索引始终为零。
{
  Array.from({length: 5}, (_, idx) => (
    <li
      className={`${styles.pageItem} ${idx === 0 ? styles.currentPage : ''}`}
      onClick={() => handlePageChange(pageIndex + idx)}
    >
      {pageIndex + idx + 1}
    </li>
  ))
}
Enter fullscreen mode Exit fullscreen mode

总页面价值小于5加上pageIndex价值。

在这种情况下,我们需要实现以下这些要求:

  • 从当前页面索引到总页面值计算出来的右侧页面数不等于5,所以需要取左侧剩余的页面数,起始索引就是左侧页面数中的第一个页面。
  • 总页数:5。
  • 当前页面索引始终为零。
const PAGE_TOTAL = 20

const Home: NextPage = () => {
  const [pageIndex, setPageIndex] = React.useState(17)

  function handlePageChange(idx: number) {
    setPageIndex(idx)
  }

  // the right pages: 18,19,20
  // the left pages: 16,17
  // the start index: 15(page 16)
  const numberOfRightPages = PAGE_TOTAL - pageIndex
  const numberOfLeftPages = 5 - numberOfRightPages
  const startPageIndex = pageIndex - numberOfLeftPages

  return (
    <div className={styles.container}>
      <ul className={styles.pagination}>
        {Array.from({length: 5}, (_, idx) => (
          <li
            key={`pagination-items-${idx}`}
            className={`${styles.pageItem} ${
              startPageIndex + idx === pageIndex ? styles.currentPage : ''
            }`}
            onClick={() => handlePageChange(startPageIndex + idx)}
          >
            {startPageIndex + idx + 1}
          </li>
        ))}
      </ul>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

下一页更改

改进

我们有三种情况,考虑这些情况,有四种不同的逻辑。

  1. 页数。
  2. 当前页面检查。
  3. 页面索引。
  4. 总页数。

我们可以通过使用方法编写组件来改进我们的代码
Inversion of Control

import type {NextPage} from 'next'
import styles from '../styles/Home.module.css'
import * as React from 'react'

const PAGE_TOTAL = 20
const NUMBER_PAGE = 5

function Pagination({
  length,
  isCurrentPage,
  goToNewPage,
  makeNumberPage,
}: {
  length: number
  isCurrentPage: (idx: number) => boolean
  goToNewPage: (idx: number) => void
  makeNumberPage: (idx: number) => number
}) {
  return (
    <ul className={styles.pagination}>
      {Array.from({length}, (_, idx) => (
        <li
          className={`${styles.pageItem} ${
            isCurrentPage(idx) ? styles.currentPage : ''
          }`}
          onClick={() => goToNewPage(idx)}
        >
          {makeNumberPage(idx)}
        </li>
      ))}
    </ul>
  )
}

const Home: NextPage = () => {
  const [pageIndex, setPageIndex] = React.useState(0)

  function handlePageChange(idx: number) {
    setPageIndex(idx)
  }

  if (PAGE_TOTAL < NUMBER_PAGE) {
    return (
      <Pagination
        length={PAGE_TOTAL}
        isCurrentPage={(idx) => idx === pageIndex}
        goToNewPage={(idx) => handlePageChange(idx)}
        makeNumberPage={(idx) => idx + 1}
      />
    )
  }

  if (PAGE_TOTAL >= pageIndex + NUMBER_PAGE) {
    return (
      <Pagination
        length={NUMBER_PAGE}
        isCurrentPage={(idx) => idx === 0}
        goToNewPage={(idx) => handlePageChange(pageIndex + idx)}
        makeNumberPage={(idx) => pageIndex + idx + 1}
      />
    )
  }

  if (PAGE_TOTAL < pageIndex + NUMBER_PAGE) {
    // the right pages: 18,19,20
    // the left pages: 16,17
    // the start index: 15(page 16)
    const numberOfRightPages = PAGE_TOTAL - pageIndex
    const numberOfLeftPages = NUMBER_PAGE - numberOfRightPages
    const startPageIndex = pageIndex - numberOfLeftPages

    return (
      <Pagination
        length={NUMBER_PAGE}
        isCurrentPage={(idx) => startPageIndex + idx === pageIndex}
        goToNewPage={(idx) => handlePageChange(startPageIndex + idx)}
        makeNumberPage={(idx) => startPageIndex + idx + 1}
      />
    )
  }

  throw new Error(`Just avoid the error comes from typescript!`)
}

export default Home
Enter fullscreen mode Exit fullscreen mode

我们有了新的分页!

五页更换

您可以更改该NUMBER_PAGE值,例如:我将其更改为 7。

七页变化

结论

我们刚刚在 React 应用中创建了分页功能。在第一个示例中,一切看起来都很简单,但如果我们要为分页添加一些功能,则需要的代码量会比我们想象的要多。所以,为什么不想想你的分页方案,然后尝试一下呢?

文章来源:https://dev.to/thangphan37/build-this-pagination-in-your-react-5h8m
PREV
JavaScript 中的回调、Promises 和 Async/Await
NEXT
TIP: Never leave your email address raw in the mailto link! Here's what to do instead This is how email harvesting works Encode your email address Let's do it!