如何在 React 中实现分页
Posted
技术标签:
【中文标题】如何在 React 中实现分页【英文标题】:How to implement pagination in React 【发布时间】:2017-03-07 01:58:48 【问题描述】:我是 ReactJS 的新手,正在其中创建一个简单的 TODO 应用程序。实际上,它是一个非常基本的应用程序,没有数据库连接,任务存储在一个数组中。我添加了编辑和删除功能,现在我想添加分页。
如何实现它?任何帮助将不胜感激。谢谢...!!
【问题讨论】:
可以在 Github 中搜索关键字reactjs todo
。你会得到很多样本。我建议你先研究它们
是的,我提到了上述功能。但是对于分页,我没有找到任何帮助。
[反应分页] (github.com/AdeleD/react-paginate)
我在纯 reactjs 中创建了分页:***.com/questions/51982376/…
【参考方案1】:
这是一种从 react-bootstrap 库创建自定义分页组件的方法,您可以在整个项目中使用该组件
您的分页组件(pagination.jsx 或 js)
import React, Component from "react";
import Pagination from "react-bootstrap";
import PropTypes from "prop-types";
export default class PaginationHandler extends Component
constructor(props)
super(props);
this.state =
paging:
offset: 0,
limit: 10
,
active: 0
;
pagingHandler = () =>
let offset = parseInt(event.target.id);
this.setState(
active: offset
);
this.props.pageHandler(event.target.id - 1); ;
nextHandler = () =>
let active = this.state.active;
this.setState(
active: active + 1
);
this.props.pageHandler(active + 1); ;
backHandler = () =>
let active = this.state.active;
this.setState(
active: active - 1
);
this.props.pageHandler(active - 1); ;
renderPageNumbers = (pageNumbers, totalPages) =>
let active = this.state;
return (
<Pagination>
<Pagination.Prev disabled=active < 5 onClick= active >5 && this.backHandler />
pageNumbers.map(number =>
if (
number >= parseInt(active) - 3 &&
number <= parseInt(active) + 3
)
return (
<Pagination.Item
id=number
active=number == active
onClick=this.pagingHandler
>
number
</Pagination.Item>
);
else
return null;
)
<Pagination.Next onClick= active <= totalPages -4 && this.nextHandler />
</Pagination>
); ;
buildComponent = (props, state) =>
const totalPages = props;
const pageNumbers = [];
for (let i = 1; i <= totalPages; i++)
pageNumbers.push(i);
return (
<div className="pull-right">
this.renderPageNumbers(pageNumbers ,totalPages)
</div>
);
;
render()
return this.buildComponent(this.props, this.state);
PaginationHandler.propTypes =
paging: PropTypes.object,
pageHandler: PropTypes.func,
totalPages: PropTypes.object
;
在父组件中使用上述子组件,如下所示
import Pagination from "../pagination";
pageHandler = (offset) =>
this.setState(( paging ) => (
paging: ...paging, offset: offset
));
render()
return (
<div>
<Pagination
paging = paging
pageHandler = this.pageHandler
totalPages = totalPages>
</Pagination>
</div>
);
【讨论】:
我的表项在我的表区域中没有变化?【参考方案2】:请在codeandbox中查看此代码示例
https://codesandbox.io/s/pagino-13pit
import Pagino from "pagino";
import useState, useMemo from "react";
export default function App()
const [pages, setPages] = useState([]);
const pagino = useMemo(() =>
const _ = new Pagino(
showFirst: false,
showLast: false,
onChange: (page, count) => setPages(_.getPages())
);
_.setCount(10);
return _;
, []);
const hanglePaginoNavigation = (type) =>
if (typeof type === "string")
pagino[type]?.();
return;
pagino.setPage(type);
;
return (
<div>
<h1>Page: pagino.page</h1>
<ul>
pages.map((page) => (
<button key=page onClick=() => hanglePaginoNavigation(page)>
page
</button>
))
</ul>
</div>
);
【讨论】:
【参考方案3】:我已经在 ReactJs 中实现了分页+搜索,看输出: Pagination in React
在 GitHub 上查看完整代码:https://github.com/navanathjadhav/generic-pagination
分页的分步实现也可以看这篇文章:https://everblogs.com/react/3-simple-steps-to-add-pagination-in-react/
【讨论】:
【参考方案4】:确保将其作为单独的组件 我用过tabler-react
import * as React from "react";
import Page, Button from "tabler-react";
class PaginateComponent extends React.Component
constructor(props)
super(props);
this.state =
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
limit: 5, // optional
page: 1
;
paginateValue = (page) =>
this.setState( page: page );
console.log(page) // access this value from parent component
paginatePrevValue = (page) =>
this.setState( page: page );
console.log(page) // access this value from parent component
paginateNxtValue = (page) =>
this.setState( page: page );
console.log(page) // access this value from parent component
render()
return (
<div>
<div>
<Button.List>
<Button
disabled=this.state.page === 0
onClick=() => this.paginatePrevValue(this.state.page - 1)
outline
color="primary"
>
Previous
</Button>
this.state.array.map((value, index) =>
return (
<Button
onClick=() => this.paginateValue(value)
color=
this.state.page === value
? "primary"
: "secondary"
>
value
</Button>
);
)
<Button
onClick=() => this.paginateNxtValue(this.state.page + 1)
outline
color="secondary"
>
Next
</Button>
</Button.List>
</div>
</div>
)
export default PaginateComponent;
【讨论】:
【参考方案5】:一个用于渲染分页的 ReactJS 哑组件。你也可以使用这个库:
https://www.npmjs.com/package/react-js-pagination
这里有一些例子:
http://vayser.github.io/react-js-pagination/
或
你也可以使用这个https://codepen.io/PiotrBerebecki/pen/pEYPbY
【讨论】:
【参考方案6】:我尝试重新创建piotr-berebecki 给出的简单分页示例,这很棒。但是当页面很多时,分页就会在屏幕上溢出。因此,我使用了前后按钮以及前进和后退按钮在页面之间来回播放。对于设计部分,我使用了 bootstrap 3。
您可以使用 pagebound 值自定义要在分页中显示的页面数量。确保对 upperPageBound 和 pageBound 使用相同的值。
class TodoApp extends React.Component
constructor()
super();
this.state =
todos: ['a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z'],
currentPage: 1,
todosPerPage: 3,
upperPageBound: 3,
lowerPageBound: 0,
isPrevBtnActive: 'disabled',
isNextBtnActive: '',
pageBound: 3
;
this.handleClick = this.handleClick.bind(this);
this.btnDecrementClick = this.btnDecrementClick.bind(this);
this.btnIncrementClick = this.btnIncrementClick.bind(this);
this.btnNextClick = this.btnNextClick.bind(this);
this.btnPrevClick = this.btnPrevClick.bind(this);
// this.componentDidMount = this.componentDidMount.bind(this);
this.setPrevAndNextBtnClass = this.setPrevAndNextBtnClass.bind(this);
componentDidUpdate()
$("ul li.active").removeClass('active');
$('ul li#'+this.state.currentPage).addClass('active');
handleClick(event)
let listid = Number(event.target.id);
this.setState(
currentPage: listid
);
$("ul li.active").removeClass('active');
$('ul li#'+listid).addClass('active');
this.setPrevAndNextBtnClass(listid);
setPrevAndNextBtnClass(listid)
let totalPage = Math.ceil(this.state.todos.length / this.state.todosPerPage);
this.setState(isNextBtnActive: 'disabled');
this.setState(isPrevBtnActive: 'disabled');
if(totalPage === listid && totalPage > 1)
this.setState(isPrevBtnActive: '');
else if(listid === 1 && totalPage > 1)
this.setState(isNextBtnActive: '');
else if(totalPage > 1)
this.setState(isNextBtnActive: '');
this.setState(isPrevBtnActive: '');
btnIncrementClick()
this.setState(upperPageBound: this.state.upperPageBound + this.state.pageBound);
this.setState(lowerPageBound: this.state.lowerPageBound + this.state.pageBound);
let listid = this.state.upperPageBound + 1;
this.setState( currentPage: listid);
this.setPrevAndNextBtnClass(listid);
btnDecrementClick()
this.setState(upperPageBound: this.state.upperPageBound - this.state.pageBound);
this.setState(lowerPageBound: this.state.lowerPageBound - this.state.pageBound);
let listid = this.state.upperPageBound - this.state.pageBound;
this.setState( currentPage: listid);
this.setPrevAndNextBtnClass(listid);
btnPrevClick()
if((this.state.currentPage -1)%this.state.pageBound === 0 )
this.setState(upperPageBound: this.state.upperPageBound - this.state.pageBound);
this.setState(lowerPageBound: this.state.lowerPageBound - this.state.pageBound);
let listid = this.state.currentPage - 1;
this.setState( currentPage : listid);
this.setPrevAndNextBtnClass(listid);
btnNextClick()
if((this.state.currentPage +1) > this.state.upperPageBound )
this.setState(upperPageBound: this.state.upperPageBound + this.state.pageBound);
this.setState(lowerPageBound: this.state.lowerPageBound + this.state.pageBound);
let listid = this.state.currentPage + 1;
this.setState( currentPage : listid);
this.setPrevAndNextBtnClass(listid);
render()
const todos, currentPage, todosPerPage,upperPageBound,lowerPageBound,isPrevBtnActive,isNextBtnActive = this.state;
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);
const renderTodos = currentTodos.map((todo, index) =>
return <li key=index>todo</li>;
);
// Logic for displaying page numbers
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(todos.length / todosPerPage); i++)
pageNumbers.push(i);
const renderPageNumbers = pageNumbers.map(number =>
if(number === 1 && currentPage === 1)
return(
<li key=number className='active' id=number><a href='#' id=number onClick=this.handleClick>number</a></li>
)
else if((number < upperPageBound + 1) && number > lowerPageBound)
return(
<li key=number id=number><a href='#' id=number onClick=this.handleClick>number</a></li>
)
);
let pageIncrementBtn = null;
if(pageNumbers.length > upperPageBound)
pageIncrementBtn = <li className=''><a href='#' onClick=this.btnIncrementClick> … </a></li>
let pageDecrementBtn = null;
if(lowerPageBound >= 1)
pageDecrementBtn = <li className=''><a href='#' onClick=this.btnDecrementClick> … </a></li>
let renderPrevBtn = null;
if(isPrevBtnActive === 'disabled')
renderPrevBtn = <li className=isPrevBtnActive><span id="btnPrev"> Prev </span></li>
else
renderPrevBtn = <li className=isPrevBtnActive><a href='#' id="btnPrev" onClick=this.btnPrevClick> Prev </a></li>
let renderNextBtn = null;
if(isNextBtnActive === 'disabled')
renderNextBtn = <li className=isNextBtnActive><span id="btnNext"> Next </span></li>
else
renderNextBtn = <li className=isNextBtnActive><a href='#' id="btnNext" onClick=this.btnNextClick> Next </a></li>
return (
<div>
<ul>
renderTodos
</ul>
<ul id="page-numbers" className="pagination">
renderPrevBtn
pageDecrementBtn
renderPageNumbers
pageIncrementBtn
renderNextBtn
</ul>
</div>
);
ReactDOM.render(
<TodoApp />,
document.getElementById('app')
);
工作演示链接:https://codepen.io/mhmanandhar/pen/oEWBqx
图片:simple react pagination
【讨论】:
工作就像一个魅力【参考方案7】:我最近创建了有助于处理分页情况的库,例如:
在 Redux 中存储标准化数据 根据搜索过滤器缓存页面 简化反应虚拟化列表用法 在后台刷新结果 存储上次访问的页面和使用的过滤器DEMO页面实现了以上所有功能。
你可以在Github找到源代码
【讨论】:
【参考方案8】: Sample pagination react js working code
import React, Component from 'react';
import
Pagination,
PaginationItem,
PaginationLink
from "reactstrap";
let prev = 0;
let next = 0;
let last = 0;
let first = 0;
export default class SamplePagination extends Component
constructor()
super();
this.state =
todos: ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','T','v','u','w','x','y','z'],
currentPage: 1,
todosPerPage: 3,
;
this.handleClick = this.handleClick.bind(this);
this.handleLastClick = this.handleLastClick.bind(this);
this.handleFirstClick = this.handleFirstClick.bind(this);
handleClick(event)
event.preventDefault();
this.setState(
currentPage: Number(event.target.id)
);
handleLastClick(event)
event.preventDefault();
this.setState(
currentPage:last
);
handleFirstClick(event)
event.preventDefault();
this.setState(
currentPage:1
);
render()
let todos, currentPage, todosPerPage = this.state;
// Logic for displaying current todos
let indexOfLastTodo = currentPage * todosPerPage;
let indexOfFirstTodo = indexOfLastTodo - todosPerPage;
let currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);
prev = currentPage > 0 ? (currentPage -1) :0;
last = Math.ceil(todos.length/todosPerPage);
next = (last === currentPage) ?currentPage: currentPage +1;
// Logic for displaying page numbers
let pageNumbers = [];
for (let i = 1; i <=last; i++)
pageNumbers.push(i);
return (
<div>
<ul>
currentTodos.map((todo,index) =>
return <li key=index>todo</li>;
)
</ul><ul id="page-numbers">
<nav>
<Pagination>
<PaginationItem>
prev === 0 ? <PaginationLink disabled>First</PaginationLink> :
<PaginationLink onClick=this.handleFirstClick id=prev href=prev>First</PaginationLink>
</PaginationItem>
<PaginationItem>
prev === 0 ? <PaginationLink disabled>Prev</PaginationLink> :
<PaginationLink onClick=this.handleClick id=prev href=prev>Prev</PaginationLink>
</PaginationItem>
pageNumbers.map((number,i) =>
<Pagination key= i>
<PaginationItem active = pageNumbers[currentPage-1] === (number) ? true : false >
<PaginationLink onClick=this.handleClick href=number key=number id=number>
number
</PaginationLink>
</PaginationItem>
</Pagination>
)
<PaginationItem>
currentPage === last ? <PaginationLink disabled>Next</PaginationLink> :
<PaginationLink onClick=this.handleClick id=pageNumbers[currentPage] href=pageNumbers[currentPage]>Next</PaginationLink>
</PaginationItem>
<PaginationItem>
currentPage === last ? <PaginationLink disabled>Last</PaginationLink> :
<PaginationLink onClick=this.handleLastClick id=pageNumbers[currentPage] href=pageNumbers[currentPage]>Last</PaginationLink>
</PaginationItem>
</Pagination>
</nav>
</ul>
</div>
);
ReactDOM.render(
<SamplePagination />,
document.getElementById('root')
);
【讨论】:
【参考方案9】:我最近创建了这个分页组件,它实现了类似于 Google 搜索结果的分页逻辑:
import React, PropTypes from 'react';
const propTypes =
items: PropTypes.array.isRequired,
onChangePage: PropTypes.func.isRequired,
initialPage: PropTypes.number
const defaultProps =
initialPage: 1
class Pagination extends React.Component
constructor(props)
super(props);
this.state = pager: ;
componentWillMount()
this.setPage(this.props.initialPage);
setPage(page)
var items = this.props.items;
var pager = this.state.pager;
if (page < 1 || page > pager.totalPages)
return;
// get new pager object for specified page
pager = this.getPager(items.length, page);
// get new page of items from items array
var pageOfItems = items.slice(pager.startIndex, pager.endIndex + 1);
// update state
this.setState( pager: pager );
// call change page function in parent component
this.props.onChangePage(pageOfItems);
getPager(totalItems, currentPage, pageSize)
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10)
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
else
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6)
startPage = 1;
endPage = 10;
else if (currentPage + 4 >= totalPages)
startPage = totalPages - 9;
endPage = totalPages;
else
startPage = currentPage - 5;
endPage = currentPage + 4;
// calculate start and end item indexes
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
// create an array of pages to ng-repeat in the pager control
var pages = _.range(startPage, endPage + 1);
// return object with all pager properties required by the view
return
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
;
render()
var pager = this.state.pager;
return (
<ul className="pagination">
<li className=pager.currentPage === 1 ? 'disabled' : ''>
<a onClick=() => this.setPage(1)>First</a>
</li>
<li className=pager.currentPage === 1 ? 'disabled' : ''>
<a onClick=() => this.setPage(pager.currentPage - 1)>Previous</a>
</li>
pager.pages.map((page, index) =>
<li key=index className=pager.currentPage === page ? 'active' : ''>
<a onClick=() => this.setPage(page)>page</a>
</li>
)
<li className=pager.currentPage === pager.totalPages ? 'disabled' : ''>
<a onClick=() => this.setPage(pager.currentPage + 1)>Next</a>
</li>
<li className=pager.currentPage === pager.totalPages ? 'disabled' : ''>
<a onClick=() => this.setPage(pager.totalPages)>Last</a>
</li>
</ul>
);
Pagination.propTypes = propTypes;
Pagination.defaultProps
export default Pagination;
下面是一个示例 App 组件,它使用 Pagination 组件对包含 150 个示例项目的列表进行分页:
import React from 'react';
import Pagination from './Pagination';
class App extends React.Component
constructor()
super();
// an example array of items to be paged
var exampleItems = _.range(1, 151).map(i => return id: i, name: 'Item ' + i ; );
this.state =
exampleItems: exampleItems,
pageOfItems: []
;
// bind function in constructor instead of render (https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md)
this.onChangePage = this.onChangePage.bind(this);
onChangePage(pageOfItems)
// update state with new page of items
this.setState( pageOfItems: pageOfItems );
render()
return (
<div>
<div className="container">
<div className="text-center">
<h1>React - Pagination Example with logic like Google</h1>
this.state.pageOfItems.map(item =>
<div key=item.id>item.name</div>
)
<Pagination items=this.state.exampleItems onChangePage=this.onChangePage />
</div>
</div>
<hr />
<div className="credits text-center">
<p>
<a href="http://jasonwatmore.com" target="_top">JasonWatmore.com</a>
</p>
</div>
</div>
);
export default App;
欲了解更多详情和现场演示,您可以查看this post
【讨论】:
使用npmjs.com/package/react-js-pagination之类的东西而不是重新发明***不是更好吗? @Jason 我在 git repo 上提出了你的实现中的一个问题,你能看看吗? github.com/cornflourblue/react-pagination-example/issues/8【参考方案10】:我最近在纯 React JS 中实现了分页。这是一个工作演示:http://codepen.io/PiotrBerebecki/pen/pEYPbY
您当然必须调整逻辑和页码的显示方式,以满足您的要求。
完整代码:
class TodoApp extends React.Component
constructor()
super();
this.state =
todos: ['a','b','c','d','e','f','g','h','i','j','k'],
currentPage: 1,
todosPerPage: 3
;
this.handleClick = this.handleClick.bind(this);
handleClick(event)
this.setState(
currentPage: Number(event.target.id)
);
render()
const todos, currentPage, todosPerPage = this.state;
// Logic for displaying todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);
const renderTodos = currentTodos.map((todo, index) =>
return <li key=index>todo</li>;
);
// Logic for displaying page numbers
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(todos.length / todosPerPage); i++)
pageNumbers.push(i);
const renderPageNumbers = pageNumbers.map(number =>
return (
<li
key=number
id=number
onClick=this.handleClick
>
number
</li>
);
);
return (
<div>
<ul>
renderTodos
</ul>
<ul id="page-numbers">
renderPageNumbers
</ul>
</div>
);
ReactDOM.render(
<TodoApp />,
document.getElementById('app')
);
【讨论】:
谢谢你,Piotr Berebecki。这真的是最简单和最好的答案.. 非常感谢...!!! 另一个在 React 中构建分页列表的动手教程:robinwieruch.de/react-paginated-list 超出预期:) 我不明白在调用 handleClick 方法后如何重新加载元素列表?我正在尝试刷新列表而不重新加载页面。非常感谢。 看这里:hemanta.io/…【参考方案11】:给你一个分页组件,新手可能有点难以理解react
:
https://www.npmjs.com/package/pagination
【讨论】:
是的..对于新手来说有点困难。无论如何感谢您的帮助..:-)以上是关于如何在 React 中实现分页的主要内容,如果未能解决你的问题,请参考以下文章