Fatih mzm
Fatih mzm

Reputation: 395

reactjs paging through the table

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

Answers (2)

Roman Mahotskyi
Roman Mahotskyi

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

Zohaib Ijaz
Zohaib Ijaz

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

Related Questions