antd Pagination 受控模式完全指南:受控页码的用法、核心 API 与源码实现解析
antd Pagination 受控模式完全指南受控页码的用法、核心 API 与源码实现解析【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design导读controlled.md是 antd Pagination 组件的“受控”示例文档主题虽短却对应着分页组件最重要的一种使用形态由开发者完全接管当前页码的状态。本文以该演示为骨架完整还原受控页码的写法并结合组件源码与测试用例讲透current、defaultCurrent、onChange等核心 API 的语义以及受控模式在服务端分页场景中的实战落地方案。读完你将能够熟练编写、调试和扩展受控分页并理解其底层状态流转机制。一、什么是受控分页演示代码逐行拆解受控Controlled组件的核心特征是组件的展示状态由外部props决定任何交互变更都必须通过回调反馈给外部由外部决定是否更新状态。Pagination 的受控模式即“受控制的页码”。仓库中的演示文件 controlled.tsx 给出了完整的最小实现import React, { useState } from react; import type { PaginationProps } from antd; import { Pagination } from antd; const App: React.FC () { const [current, setCurrent] useState(3); const onChange: PaginationProps[onChange] (page) { console.log(page); setCurrent(page); }; return Pagination current{current} onChange{onChange} total{50} /; }; export default App;对照文档注释controlled.md——“受控制的页码 / Controlled page number”这段代码的要点可拆解为三部分状态上移页码状态current通过useState(3)声明在组件外部父组件初始值为第 3 页。演示刻意将初始值设为非 1用于直观体现“外部决定初始页码”的能力。双向绑定Pagination current{current} onChange{onChange} /。current把当前页码“喂”给组件用户点击页码、上一页/下一页、快速跳转Quick Jumper时触发onChange回调参数page即用户想要跳转的新页码。状态回流onChange内部调用setCurrent(page)把新页码写回 React 状态触发重渲染Pagination的选中项随之更新。由此形成一个完整闭环UI 交互 → onChange 回调 → 外部 setState → props 回流 → UI 更新。对应文档 controlled.md 只用了两句话但其背后是整个受控组件设计模式在分页场景的落地也是服务端分页后端返回数据、前端受控翻页的标准写法。二、受控 vs 非受控从 API 语义看状态归属要真正掌握受控模式需要先分清 Pagination 的两套状态入口。官方 API 表index.en-US.md中与本主题直接相关的参数如下参数说明类型默认值current当前页码number-defaultCurrent默认的初始页码number1pageSize每页条数number-defaultPageSize默认的每页条数number10total数据总条数number0onChange页码或pageSize改变时触发参数为新的页码与每页条数function(page, pageSize)-onShowSizeChangepageSize改变时触发function(current, size)-核心区分规则非受控默认形态不传current只传defaultCurrent默认 1。页码状态由 Pagination 内部维护用户点击后组件自己更新高亮页onChange仅作为“通知”告知外部发生了翻页。典型例子见 basic.tsxconst App: React.FC () Pagination defaultCurrent{1} total{50} /;受控本演示形态传入current页码状态完全由外部掌控。用户点击后组件不会自行决定最终选中页而是把意图通过onChange抛给外部——若外部不调用setState页码会“弹回”原值。这就是受控组件的经典约束外部不更新UI 不变化。从源码结构看Pagination.tsx 的PaginationProps接口直接继承自rc-pagination的RcPaginationProps第 7、20 行current、defaultCurrent、onChange等属性都定义于底层rc-paginationantd 组件本身不持有页码状态而是把剩余 props 原样透传给RcPagination第 133-143 行RcPagination {...iconsProps} {...restProps} style{mergedStyle} prefixCls{prefixCls} selectPrefixCls{selectPrefixCls} ... /这意味着受控与不受控的判别逻辑、页码高亮计算都发生在rc-pagination内部antd 层负责的是前缀类名、图标左右箭头、省略号、尺寸适配、RTL 与样式变量等上层封装。三、受控模式下 onChange 与 onShowSizeChange 的行为细节受控模式下有两个回调需要重点关注测试用例 index.test.tsx 给出了可验证的预期行为it(should onChange called when pageSize change, () { const onChange jest.fn(); const onShowSizeChange jest.fn(); const { container } render( Pagination defaultCurrent{1} total{500} onChange{onChange} onShowSizeChange{onShowSizeChange} /, ); fireEvent.mouseDown(container.querySelector(.ant-select-selector)!); expect(container.querySelectorAll(.ant-select-item-option).length).toBe(4); fireEvent.click(container.querySelectorAll(.ant-select-item-option)[1]); expect(onChange).toHaveBeenCalledWith(1, 20); });该测试确认了两个事实onChange的第二个参数是pageSize当用户通过sizeChanger切换每页条数时onChange同样会被触发且签名固定为(page, pageSize)。所以在受控模式中onChange是“页码或 pageSize 任一变化”的统一出口建议在回调里同时更新current与pageSize两个状态保证受控数据一致const [page, setPage] useState(1); const [pageSize, setPageSize] useState(10); const onChange: PaginationProps[onChange] (nextPage, nextPageSize) { setPage(nextPage); setPageSize(nextPageSize); }; return ( Pagination current{page} pageSize{pageSize} total{500} showSizeChanger onChange{onChange} / );onShowSizeChange是独立于onChange的钩子它只感知“每页条数变化”签名是(current, size)。当业务需要区分“翻页”与“改每页条数”两种动作例如只在大小时重置页码时可以同时使用两者。另外注意官方 API 中的默认行为showSizeChanger默认在total 50时为 true源码第 61 行也展示了它与ConfigProvider中全局pagination.showSizeChanger的合并逻辑showSizeChanger ?? pagination.showSizeChanger。四、源码级原理antd 如何透传与增强受控分页从源码结构可以推断出受控分页的完整调用链用户传入current/onChange到 antd 的 Pagination.tsx第 37 行起的函数组件。antd 层从 props 中解构出与自身相关的align、prefixCls、selectPrefixCls、size、responsive、showSizeChanger等其余通过...restProps第 50 行连同onChange等一起原样转发给RcPagination第 133-143 行。也就是说受控逻辑本身不在 antd 代码内antd 只做“增强转发”。rc-pagination内部根据props.current是否存在决定走受控还是非受控分支并计算展示的页码列表含省略号•••、上一页/下一页按钮等。antd 层做的增强工作包括图标注入根据directionRTL/LTR自动切换左右箭头方向第 63-102 行通过prevIcon、nextIcon、jumpPrevIcon、jumpNextIcon传入rc-pagination尺寸适配useBreakpoint(responsive)结合useSize当size未指定且窗口为小屏xs时自动切换为ant-pagination-mini第 52、110 行选择器替换用 antd 自身的MiddleSelect/MiniSelect替换rc-pagination默认的 pageSize 下拉第 140 行主题与样式通过useStyle(prefixCls)注入 CSS-in-JS 变量与 hashId第 59 行wireframe 模式下额外渲染BorderedStyle第 132 行。这些增强均不影响受控语义current由外部持有这一约束自始至终成立。五、受控分页的典型实战场景服务端分页受控模式最常见的落地场景是服务端分页数据不在前端一次性渲染而是每次翻页向后端请求对应页的数据。核心写法规避了“页码显示”与“数据内容”不一致的时序问题import React, { useState, useEffect } from react; import type { PaginationProps } from antd; import { Pagination, Table } from antd; const App: React.FC () { const [current, setCurrent] useState(1); const [pageSize, setPageSize] useState(10); const [total, setTotal] useState(0); const [data, setData] useStateRecordstring, unknown[]([]); useEffect(() { // 模拟向后端请求受控页码/每页条数直接作为查询参数 fetch(/api/list?page${current}pageSize${pageSize}) .then((res) res.json()) .then((res) { setData(res.list); setTotal(res.total); }); }, [current, pageSize]); const onChange: PaginationProps[onChange] (page, size) { setCurrent(page); setPageSize(size); }; return ( Table rowKeyid dataSource{data} pagination{false} / Pagination current{current} pageSize{pageSize} total{total} showSizeChanger showQuickJumper showTotal{(t, range) ${range[0]}-${range[1]} of ${t} items} onChange{onChange} / / ); }; export default App;关键点在于受控状态下Pagination 只负责“表达”current而“真正翻页取数”由useEffect依据[current, pageSize]驱动。快速连续点击时React 会以最后一次状态为准发起请求天然规避了非受控模式下“UI 已跳页但数据未更新”的中间态。若需求是“每页条数变化时页码重置回 1”可在onChange中判断当size ! pageSize时把page一并重置const onChange: PaginationProps[onChange] (page, size) { setPageSize(size); setCurrent(size pageSize ? page : 1); };六、测试保障演示与受控行为的自动化验证仓库为演示代码提供了两层自动化保障可放心照抄controlled.tsx的写法演示快照测试demo.test.ts 调用共享工具demoTest(pagination)它会遍历 tests/shared/demoTest.tsx 中globSync(./components/${component}/demo/*.tsx)匹配到的全部演示文件逐个 SSR 渲染并与快照比对。受控演示的渲染结果固化在 demo.test.ts.snap 中renders components/pagination/demo/controlled.tsx correctly保证演示代码可稳定渲染。交互行为测试上文引用的 index.test.tsx 覆盖了onChange参数签名、pageSize 切换触发行为、RTL 渲染、ConfigProvider尺寸继承、align对齐等是受控模式下回调语义的直接证据。七、受控模式常见误区与规避建议只传current不处理onChange此时用户点击看似“无效”页码弹回容易误判为 Bug。受控组件必须配套状态回流要么实现onChange更新外部状态要么改用非受控的defaultCurrent。混用current与defaultCurrent受控与非受控入口二选一。传入current后defaultCurrent只在初始化时兜底生效后续完全以current为准混用会造成心智混乱。忘记更新pageSize受控模式下onChange会携带新的pageSize见第三节测试证据若只setPage不setPageSize翻页后“每页条数”选择器可能与实际数据条数不一致。异步取数时竞态服务端分页下翻页过快时较早的请求可能后返回并覆盖新数据。可引入请求序号或取消机制确保以最后一次请求为准。小结受控分页的本质是把“当前页码”这一状态从组件内部提升到业务层通过currentonChange建立闭环。从仓库证据看antd 的 Pagination.tsx 不参与状态持有只负责透传与增强受控语义由底层rc-pagination保证index.test.tsx 则固化了onChange(page, pageSize)的调用约定。掌握 controlled.tsx 展示的最小闭环再配合pageSize、onShowSizeChange与异步取数即可在服务端分页等真实业务中稳定驾驭受控 Pagination。【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考