Pagination
Pagination 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 v3 Pagination 文档。本指南只关注从 HeroUI v2 的迁移。
结构变化
在 v2 中,Pagination 是单个组件,通过 props 在内部完成所有渲染:
import { Pagination } from "@heroui/react";
export default function App() {
return (
<Pagination total={10} initialPage={1} showControls />
);
}在 v3 中,Pagination 采用复合组件模式,需要显式组合各个部分:
import { Pagination } from "@heroui/react";
export default function App() {
return (
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive>1</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>2</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>3</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>10</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>
);
}主要变化
1. 组件结构
v2: 单个 Pagination 组件,通过 total prop 自动生成页码项
v3: 复合组件:Pagination、Pagination.Summary、Pagination.Content、Pagination.Item、Pagination.Link、Pagination.Previous、Pagination.PreviousIcon、Pagination.Next、Pagination.NextIcon、Pagination.Ellipsis
2. 页码生成
v2: 由 total、siblings、boundaries 等 props 自动生成页码
v3: 手动组合页码项,完全掌控布局与行为;可自行实现分页逻辑或使用分页 hook。
3. Prop 变更
| v2 prop | v3 对应 | 说明 |
|---|---|---|
total | — | 已移除(请手动组合条目) |
page | — | 通过在 Pagination.Link 上使用 isActive 管理当前页 |
initialPage | — | 请在外部自行管理状态 |
onChange | — | 请在各个 Pagination.Link 上使用 onPress |
siblings | — | 已移除(请手动组合条目) |
boundaries | — | 已移除(请手动组合条目) |
dotsJump | — | 已移除(省略号点击请自行处理) |
loop | — | 已移除(请自行实现) |
showControls | — | 请组合 Pagination.Previous 与 Pagination.Next |
isCompact | — | 已移除(请用 Tailwind CSS 控制样式) |
showShadow | — | 已移除(请使用 Tailwind shadow-* 类) |
size | Pagination 上的 size | 与 v2 相同(sm、md、lg) |
variant | — | 已移除(请使用 Tailwind CSS) |
color | — | 已移除(请使用 Tailwind CSS) |
radius | — | 已移除(请使用 Tailwind CSS) |
isDisabled | Pagination.Link、Pagination.Previous、Pagination.Next 上的 isDisabled | 按条目分别控制,而非全局 |
disableCursorAnimation | — | 已移除 |
disableAnimation | — | 已移除 |
renderItem | — | 请直接组合子节点 |
getItemAriaLabel | — | 请在各条目上设置 aria-label |
classNames | — | 请在各复合子组件上使用 className |
4. Hook 变化
v2: 提供 usePagination hook,便于自定义实现
v3: 无内置 hook——请直接组合分页项,或自行实现分页逻辑
迁移示例
基础分页
import { Pagination } from "@heroui/react";
<Pagination total={10} initialPage={1} />import { useState } from "react";
import { Pagination } from "@heroui/react";
const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
</Pagination.Content>
</Pagination>上一页 / 下一页控件
<Pagination total={10} initialPage={1} showControls />const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => Math.max(1, p - 1))}
>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => Math.min(totalPages, p + 1))}
>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>含省略号
<Pagination total={20} initialPage={1} siblings={1} boundaries={1} />const [page, setPage] = useState(1);
const totalPages = 20;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => Math.max(1, p - 1))}
>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive={page === 1} onPress={() => setPage(1)}>
1
</Pagination.Link>
</Pagination.Item>
{page > 3 && (
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
)}
{[page - 1, page, page + 1]
.filter((p) => p > 1 && p < totalPages)
.map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
{page < totalPages - 2 && (
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
)}
<Pagination.Item>
<Pagination.Link
isActive={page === totalPages}
onPress={() => setPage(totalPages)}
>
{totalPages}
</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => Math.min(totalPages, p + 1))}
>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>仅上一页 / 下一页
{/* v2 无内置的仅 prev/next 模式 */}const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => p - 1)}
>
<Pagination.PreviousIcon />
<span>Previous</span>
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => p + 1)}
>
<span>Next</span>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>使用 Pagination.Summary
{/* v2 无内置 summary 插槽 */}const [page, setPage] = useState(1);
const perPage = 10;
const total = 100;
<Pagination>
<Pagination.Summary>
Showing {(page - 1) * perPage + 1}-{Math.min(page * perPage, total)} of {total}
</Pagination.Summary>
<Pagination.Content>
{/* Pagination items */}
</Pagination.Content>
</Pagination>自定义图标
{/* v2 需借助 renderItem 才能自定义图标 */}import { Icon } from "@iconify/react";
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous onPress={() => setPage((p) => p - 1)}>
<Pagination.PreviousIcon>
<Icon icon="gravity-ui:arrow-left" />
</Pagination.PreviousIcon>
</Pagination.Previous>
</Pagination.Item>
{/* Page links */}
<Pagination.Item>
<Pagination.Next onPress={() => setPage((p) => p + 1)}>
<Pagination.NextIcon>
<Icon icon="gravity-ui:arrow-right" />
</Pagination.NextIcon>
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>样式变化
v2:classNames prop
<Pagination
classNames={{
base: "custom-base",
wrapper: "custom-wrapper",
prev: "custom-prev",
next: "custom-next",
item: "custom-item",
cursor: "custom-cursor",
ellipsis: "custom-ellipsis",
}}
/>v3:直接使用 className
<Pagination className="custom-base">
<Pagination.Content className="custom-wrapper">
<Pagination.Item className="custom-item">
<Pagination.Previous className="custom-prev">
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive className="custom-cursor">1</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Ellipsis className="custom-ellipsis" />
</Pagination.Item>
<Pagination.Item>
<Pagination.Next className="custom-next">
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>组件剖析
v3 Pagination 的结构如下:
Pagination (Root, nav element)
├── Pagination.Summary (optional, info text)
└── Pagination.Content (ul container)
└── Pagination.Item (li wrapper, repeated)
├── Pagination.Previous (with Pagination.PreviousIcon)
├── Pagination.Link (page number, isActive for current)
├── Pagination.Ellipsis
└── Pagination.Next (with Pagination.NextIcon)总结
- 组件结构:由单个自动生成式组件,改为需手动组合的复合组件。
- 页码生成:
total/siblings/boundariesprops → 结合自定义分页逻辑手动渲染页码项。 - 当前页:
page/initialPageprops → 在各个Pagination.Link上使用isActive。 - 导航控件:
showControlsprop → 直接组合Pagination.Previous与Pagination.Next。 - 省略号:由自动生成 → 在需要处手动放置
Pagination.Ellipsis。 - 事件:单一
onChange→ 各Pagination.Link上的独立onPress处理函数。 - 新能力:
Pagination.Summary用于展示结果数量;可通过PreviousIcon/NextIcon的 children 自定义图标。 - Hook 移除:
usePagination→ 请自行实现分页逻辑。 - 样式 props 移除:
variant、color、radius、isCompact、showShadow→ 请使用 Tailwind CSS。 classNames移除:请在各复合子组件上使用className。