Reputation: 395
I am listing data with api. I show the data I received to the user with reactstrap table. but I want to paging.
Up to 6 records are displayed on one page, other records are displayed on the following pages.
import React, { Component, useState } from "react";
import withAuth from "../../components/helpers/withAuth";
import {
Button,
Card,
CardBody,
CardHeader,
Col,
Pagination,
PaginationItem,
PaginationLink,
Row,
Table,
} from "reactstrap";
class CustomerDebt extends Component {
constructor(props) {
super(props);
this.domain = `http://127.0.0.1:8000`;
this.state = {
isLoaded: true,
items: [], //Customer Debt Items
};
}
async componentDidMount() {
//customer debt list
await fetch(
`${this.domain}/api/debt/list?customer=` +
this.props.customerInfo.customer.id,
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
"Content-Type": "application/json"
}
}
)
.then(res => {
if (res.ok) {
return res.json();
} else {
return res.json().then(err => Promise.reject(err));
}
})
.then(json => {
this.setState({
items: json,
});
this.abortController.abort();
})
.catch(error => {
return error;
});
}
render() {
const { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className={"animated fadeIn container-fluid"}>
<Row>
<Col>
<Card>
<CardHeader>
<i className="fa fa-align-justify" /> Müşteri Borcu
</CardHeader>
<CardBody>
<Table hover bordered striped responsive size="sm">
<thead>
<tr>
<th width={"10"} />
<th width={"15"}>No</th>
<th style={{ display: "none" }}>User</th>
<th style={{ display: "none" }}>Key</th>
<th style={{ display: "none" }}>CreatedUserKey</th>
<th width={"40"}>Total debt</th>
<th width={"40"}>Received amount</th>
<th scope={"row"}>Description</th>
<th width={"20"}>Payment Date</th>
</tr>
</thead>
<tbody>
{items.map(item => {
return (
<tr key={item.id}>
<td>{item.id}</td>
<td style={{ display: "none" }}>{item.user}</td>
<td style={{ display: "none" }}>{item.debtKey}</td>
<td style={{ display: "none" }}> {" "} {item.createduserKey}{" "} </td>
<td>{item.totalDebt}</td>
<td>{item.receivedAmount}</td>
<td>{item.description}</td>
<td> {new Date(item.paymentDate).toLocaleString()} </td>
</tr>
);
})}
</tbody>
</Table>
<nav>
<Pagination>
<PaginationItem>
<PaginationLink previous tag="button">
Back
</PaginationLink>
</PaginationItem>
<PaginationItem active>
<PaginationLink tag="button">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink tag="button">2</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink tag="button">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink tag="button">4</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink next tag="button">
Next
</PaginationLink>
</PaginationItem>
<PaginationItem></PaginationItem>
</Pagination>
</nav>
</CardBody>
</Card>
</Col>
</Row>
</div>
);
}
}
}
export default CustomerDebt;
Upvotes: 2
Views: 5911
Reputation: 6625
First of all you need to create two variables that will help you to track current page and size of the table. Let's call them pageSize
and pageIndex
class App extends React.Component {
state = {
pageSize: 2, // <- 2 items will be shown on single page
pageIndex: 0, // 0 is a default page to show
items: []
};
...
Then you need to fetch some data from the external source. In my example, I just create a mock array and set it to the state.
...
componentDidMount() {
const data = [
{ id: 1, name: "Roman" },
{ id: 2, name: "Oleh" },
{ id: 3, name: "Vitalii" },
{ id: 4, name: "Mikhail" },
{ id: 5, name: "Vladislav" },
{ id: 6, name: "Anton" },
{ id: 7, name: "Yurii" },
{ id: 8, name: "Volodymir" },
{ id: 9, name: "Taras" }
];
this.setState({ items: data });
}
...
After that, you need to create helper functions and place table navigation logic there. Let's name them handlePrevPageClick
and handleNextPageClick
.
The handlePrevPageClick
should decrease current index on click. Also, we need to prevent user from scrolling to much. So, if pageIndex
is not 0 - decrease, otherwise stop decreasing.
The handleNextPageClick
should have absolutely the same logic as handlePrevPageClick
. The only thing it is reversed. Don't let user overscroll your table. To do that, we need to understand how many pages do we have. By dividing this.state.items / this.state.pageSize
we will get the total number of available pages. Let's imagine that our table have 12 items and page size is 5. So, 12 / 5 = 2.4. It means that we will have 2 pages full loaded and 4 items left. By using Math.ceil(12 / 5)
we will get 3 - is an integer number of total available pages of the table. After that, we just add simple condition, if pageIndex
< 3, if yes - increase pageIndex
, otherwise stop.
...
handlePrevPageClick(event) {
this.setState(prevState => ({
pageIndex: prevState.pageIndex > 0 ? prevState.pageIndex - 1 : 0
}));
}
handleNextPageClick(event) {
this.setState(prevState => ({
pageIndex:
prevState.pageIndex <
Math.ceil(prevState.items.length / prevState.pageSize)
? prevState.pageIndex + 1
: prevState.pageIndex
}));
}
The last think is to render your table with correct rows. To do that, you can use .slice(...)
. First argument is a left boundary of the page, the second one is a right boundary of the page.
First page
this.state.pageIndex * this.state.pageSize, // 0 * 5 = 0
this.state.pageIndex * this.state.pageSize + this.state.pageSize // 0 * 5 + 5 = 5
To show elements from index 0 to 5.
Second page
this.state.pageIndex * this.state.pageSize, // 1 * 5 = 5
this.state.pageIndex * this.state.pageSize + this.state.pageSize // 1 * 5 + 5 = 10
To show elements from index 5 to 10.
...
render() {
return (
<>
<button onClick={event => this.handlePrevPageClick(event)}>
Prev page
</button>
<button onClick={event => this.handleNextPageClick(event)}>
Next page
</button>
<table border="1">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.state.items
.slice(
this.state.pageIndex * this.state.pageSize,
this.state.pageIndex * this.state.pageSize + this.state.pageSize
)
.map(item => (
<tr>
<td>{item.id}</td>
<td>{item.name}</td>
</tr>
))}
</tbody>
</table>
</>
);
}
}
If you want to see full working example, please use this CodeSandbox link
Upvotes: 1
Reputation: 22885
You need to generate pagination buttons dynamically based on number of records and then on pressing pagination button, set the page number and create an array if items that you want to show based on page number and per page size.
This is sample code to give you an idea how to get this done. This is not complete or bug proof since it is your job. I hope will get the idea.
class CustomerDebt extends Component {
constructor(props) {
super(props);
this.domain = `http://127.0.0.1:8000`;
this.state = {
isLoaded: true,
items: [], //Customer Debt Items,
pageItems: [],
page: 0,
pageSize: 6
};
}
async componentDidMount() {
const { pageSize } = this.state;
//customer debt list
await fetch(
`${this.domain}/api/debt/list?customer=` +
this.props.customerInfo.customer.id,
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
"Content-Type": "application/json"
}
}
)
.then(res => {
if (res.ok) {
return res.json();
} else {
return res.json().then(err => Promise.reject(err));
}
})
.then(json => {
this.setState({
items: json,
pageItems: json.slice(0, pageSize)
});
this.abortController.abort();
})
.catch(error => {
return error;
});
}
render() {
const { isLoaded, pageItems, items, page, pageSize } = this.state;
const pages = Math.ceil(items.length / page);
const paginationItems = Array(pages).fill('').map((i, index) => (
<PaginationItem active={page === index}>
<PaginationLink tag="button" onClick={() => this.setState({page: index })}}>2</PaginationLink>
</PaginationItem>
));
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className={"animated fadeIn container-fluid"}>
<Row>
<Col>
<Card>
<CardHeader>
<i className="fa fa-align-justify" /> Müşteri Borcu
</CardHeader>
<CardBody>
<Table hover bordered striped responsive size="sm">
<thead>
<tr>
<th width={"10"} />
<th width={"15"}>No</th>
<th style={{ display: "none" }}>User</th>
<th style={{ display: "none" }}>Key</th>
<th style={{ display: "none" }}>CreatedUserKey</th>
<th width={"40"}>Total debt</th>
<th width={"40"}>Received amount</th>
<th scope={"row"}>Description</th>
<th width={"20"}>Payment Date</th>
</tr>
</thead>
<tbody>
{pageItems.map(item => {
return (
<tr key={item.id}>
<td>{item.id}</td>
<td style={{ display: "none" }}>{item.user}</td>
<td style={{ display: "none" }}>{item.debtKey}</td>
<td style={{ display: "none" }}> {" "} {item.createduserKey}{" "} </td>
<td>{item.totalDebt}</td>
<td>{item.receivedAmount}</td>
<td>{item.description}</td>
<td> {new Date(item.paymentDate).toLocaleString()} </td>
</tr>
);
})}
</tbody>
</Table>
<nav>
<Pagination>
<PaginationItem onClick={() => this.setState(prev => ({page: prev.page -1}))}>
<PaginationLink>
Back
</PaginationLink>
<PaginationItem onClick={() => this.setState(prev => ({page: prev.page + 1}))}>
<PaginationLink next tag="button">
Next
</PaginationLink>
</PaginationItem>
<PaginationItem></PaginationItem>
</Pagination>
</nav>
</CardBody>
</Card>
</Col>
</Row>
</div>
);
}
}
}
export default CustomerDebt;
Upvotes: 2