We now have a youtube channel. Subscribe!

ReactJS | Pagination

ReactJS | Pagination


Hello folks! welcome back to a new section of our tutorial on ReactJS. In this section of our tutorial on ReactJS, we will be studying about ReactJS Pagination.

Pagination is one of the distinct features of React needing unique logic and at the same time, the user interface rendering may vary drastically. For example, the pagination can be done for a tabular content and also for a gallery content. Pagination component will perform the pagination logic and delegates the rendering logic to other components. In this article, we will try creating a pagination component called (Pager) for our Expense manager application using Render props.

Open expense-manager application in your favorite editor.

Next, create a file, Pager.css beneath the src/component to add styles for pagination component.

a{
   text-decoration: none;
}
p, li, a{
   font-size: 12px;
} 
.container{
   width: 720px;
   max-width: 340px;
   margin: 0 auto;
   position: relative;
   text-align: center;
}
.pagination{
   padding: 14px 0;
}
.pagination ul{
   margin: 0 auto; 
   padding: 0;
   list-style-type: none;
   text-align: left;
}
.pagination a{
   display: inline-block;
   padding: 10px 18px;
   color: #222;
}
.p1 a{
   width: 30px;
   height: 30px;
   line-height: 30px;
   padding: 0;
   text-align: center;
}
.p1 a.is-active{
   background-color: #887cb8;
   border-radius: 25%;
   color: #fff;
}

Next, make a file, Pager.js in src/component folder to create Pager component and then start editing.

Next, import React library.

import React from 'react';

Next, import pagination stylesheet.

import './Pager.css'


Next, create a class, Pager and then call the constructor with props.

class Pager extends React.Component { 
   constructor(props) { 
      super(props); 
   } 
}

Next, initialize the state of the component with expense items (items) and number of items to show in a page (pageCount).

this.state = { 
   items: this.props.items, 
   pageCount: this.props.pageCount 
}

Write a function, calculate(), which accepts current state and the current page number. It calculates the total of the available pages based on page count and the items to show in current page. Lastly, the computed values is returned.

calculate(state, pageNo) {
   let currentPage = pageNo;
   let totalPages = Math.ceil(state.items.length / state.pageCount);
   if(currentPage > totalPages) currentPage = totalPages;
   let hasPreviousPage = currentPage == 1 ? false : true;
   let hasNextPage = currentPage == totalPages ? false : true;
   let first = (currentPage - 1) * state.pageCount
   let last = first + state.pageCount;
   let filteredItems = state.items.slice(first, last)

   let newState = {
      items: state.items,
      filteredItems: filteredItems,
      currentPage: currentPage,
      totalPages: totalPages,
      pageCount: state.pageCount
   }
   return newState;
}

Next, call calculate() method in constructor to get the initial details about pagination.

this.state = this.calculate(this.state, 1);

Write an event handler method handleClick() to handle the page navigation events of the of the pagination component.

handleClick(pageNo, e) { 
   e.preventDefault(); 
   this.setState((state, props) => { 
      return this.calculate(state, pageNo); 
   }) 
}

Next, write an event handler to remove the expense item. Since, the pager component handles all the expense items, the remove feature should be moved here.

handleDelete = (id, e) => {
   e.preventDefault();
   console.log(id);
   this.setState((state, props) => {
      let items = [];

      state.items.forEach((item, idx) => {
         if (item.id != id)
            items.push(item)
      })
      let newState = {
         items: items
      }
      return newState;
   })
   this.setState((state, props) => {
      return this.calculate(state, this.state.currentPage);
   })
}

Next, create a render() method to create the pagination's user interface and then call the Render Props by passing the items required to render the current page.

render() {
   let pageArray = new Array();
   let i = 1;
   for (i = 1; i <= this.state.totalPages; i++)
      pageArray.push(i)

   const pages = pageArray.map((idx) =>
      <a href="#" key={idx} onClick={this.handleClick.bind(this, idx)} className={idx == this.state.currentPage ? "is-active" : ""}><li>{idx}</li></a>
   );
   let propsToPass = {
      items: this.state.filteredItems,
      deleteHandler: this.handleDelete
   }
   return (
      <div>
         {this.props.render(propsToPass)}
         <div style={{ width: 720, margin: 0 }}>
            <div className="container">
               <div className="pagination p1">
                  <ul>
                     {this.state.currentPage != 1 ? <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage - 1)}><li><</li></a> : <span> </span>}
                     {pages}
                     {this.state.currentPage != this.state.totalPages ?
                     <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage + 1)}><li>></li></a> : <span> </span>}
                  </ul>
               </div>
            </div>
         </div>
      </div>
   )
}

Here,

  • Used map to create the pagination button.
  • Used conditional rendering to show and hide first and last page.
  • Used this.props.render to call and render the passed component.


Finally, export the component.

export default Pager

The complete code of the Pager component is as follows -

import React from 'react';
import './Pager.css'

class Pager extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         items: this.props.items,
         pageCount: this.props.pageCount,
      }
      this.state = this.calculate(this.state, 1);
   }
   calculate(state, pageNo) {
      let currentPage = pageNo;
      let totalPages = Math.ceil(state.items.length / state.pageCount);
      if(currentPage > totalPages) currentPage = totalPages;
      let hasPreviousPage = currentPage == 1 ? false : true;
      let hasNextPage = currentPage == totalPages ? false : true;
      let first = (currentPage - 1) * state.pageCount
      let last = first + state.pageCount;
      let filteredItems = state.items.slice(first, last)
      let newState = {
         items: state.items,
         filteredItems: filteredItems,
         currentPage: currentPage,
         totalPages: totalPages,
         pageCount: state.pageCount
      }
      return newState;
   }
   handleClick(pageNo, e) {
      e.preventDefault();
      this.setState((state, props) => {
         return this.calculate(state, pageNo);
      })
   }
   handleDelete = (id, e) => {
      e.preventDefault();
      console.log(id);

      this.setState((state, props) => {
         let items = [];
         state.items.forEach((item, idx) => {
            if (item.id != id)
               items.push(item)
         })
         let newState = {
            items: items
         }
         return newState;
      })
      this.setState((state, props) => {
         return this.calculate(state, this.state.currentPage);
      })
   }
   render() {
      let pageArray = new Array();
      let i = 1;
      for (i = 1; i <= this.state.totalPages; i++)pageArray.push(i)
      
      const pages = pageArray.map((idx) =>
         <a href="#" key={idx} onClick={this.handleClick.bind(this, idx)} className={idx == this.state.currentPage ? "is-active" : ""}><li>{idx}</li></a>
      );
      let propsToPass = {
         items: this.state.filteredItems,
         deleteHandler: this.handleDelete
      }
      return (
         <div>
            {this.props.render(propsToPass)}
   
            <div style={{ width: 720, margin: 0 }}>
               <div className="container">
                  <div className="pagination p1">
                     <ul>
                        {this.state.currentPage != 1 ? <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage - 1)}><li><</li></a> : <span> </span>}
                        {pages}
                        {this.state.currentPage != this.state.totalPages ?
                        <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage + 1)}><li>></li></a> : <span> </span>}
                     </ul>
                  </div>
               </div>
            </div>
         </div>
      )
   }
}
export default Pagerimport React from 'react';
import './Pager.css'

class Pager extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         items: this.props.items,
         pageCount: this.props.pageCount,
      }
      this.state = this.calculate(this.state, 1);
   }
   calculate(state, pageNo) {
      let currentPage = pageNo;
      let totalPages = Math.ceil(state.items.length / state.pageCount);

      if(currentPage > totalPages)currentPage = totalPages;
         
      let hasPreviousPage = currentPage == 1 ? false : true;
      let hasNextPage = currentPage == totalPages ? false : true;
      let first = (currentPage - 1) * state.pageCount
      let last = first + state.pageCount;
      let filteredItems = state.items.slice(first, last)
   
      let newState = {
         items: state.items,
         filteredItems: filteredItems,
         currentPage: currentPage,
         totalPages: totalPages,
         pageCount: state.pageCount
      }
      return newState;
   }
   handleClick(pageNo, e) {
      e.preventDefault();
   
      this.setState((state, props) => {
         return this.calculate(state, pageNo);
      })
   }
   handleDelete = (id, e) => {
      e.preventDefault();
      console.log(id);

      this.setState((state, props) => {
         let items = [];

         state.items.forEach((item, idx) => {
            if (item.id != id)
               items.push(item)
         })
         let newState = {
            items: items
         }
         return newState;
      })
      this.setState((state, props) => {
         return this.calculate(state, this.state.currentPage);
      })
   }
   render() {
      let pageArray = new Array();
      let i = 1;
      for (i = 1; i <= this.state.totalPages; i++)pageArray.push(i)
   
      const pages = pageArray.map((idx) =>
         <a href="#" key={idx} onClick={this.handleClick.bind(this, idx)} className={idx == this.state.currentPage ? "is-active" : ""}><li>{idx}</li></a>
      );

      let propsToPass = {
         items: this.state.filteredItems,
         deleteHandler: this.handleDelete
      }
      return (
         <div>
            {this.props.render(propsToPass)}
   
            <div style={{ width: 720, margin: 0 }}>
               <div className="container">
                  <div className="pagination p1">
                     <ul>
                        {this.state.currentPage != 1 ? <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage - 1)}><li><</li></a> : <span> </span>}
                        {pages}
                        {this.state.currentPage != this.state.totalPages ?
                        <a href="#" onClick={this.handleClick.bind(this, this.state.currentPage + 1)}><li>></li></a> : <span> </span>}
                     </ul>
                  </div>
               </div>
            </div>
         </div>
      )
   }
}
export default Pager

Next, open ExpenseEntryItemList.js file and then update the getDerivedStateFromProps function to get the current expense item list by copying the passed-in props into the new state.

static getDerivedStateFromProps(props, state) {
   let newState = {
      items: props.items
   }
   return newState;
}

Next, update the handleDelete method and call the event handler method passed into the component from Pager component via onDelete property.

handleDelete = (id, e) => {
   e.preventDefault();

   if(this.props.onDelete != null)this.props.onDelete(id, e);
}

Remove rendering of the header and footer, if any is available in the render method.

return (
   <div>
      <div>{this.props.header}</div>
         ... existing code ...
      <div>{this.props.footer}</div>
   </div>
);


The full code of the ExpenseEntryItemList component is as follows -

import React from 'react';
import './ExpenseEntryItemList.css';

class ExpenseEntryItemList extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         items: this.props.items
      }
      this.handleMouseEnter = this.handleMouseEnter.bind();
      this.handleMouseLeave = this.handleMouseLeave.bind();
      this.handleMouseOver = this.handleMouseOver.bind();
   }
   handleMouseEnter(e) {
      e.target.parentNode.classList.add("highlight");
   }
      handleMouseLeave(e) {
      e.target.parentNode.classList.remove("highlight");
   }
   handleMouseOver(e) {
      console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
   }
   handleDelete = (id, e) => {
      e.preventDefault();

      if(this.props.onDelete != null)this.props.onDelete(id, e);
         
      /*console.log(id);

      this.setState((state, props) => {
         let items = [];
         state.items.forEach((item, idx) => {
            if (item.id != id)
               items.push(item)
         })
         let newState = {
            items: items
         }
         return newState;
      })*/
   }
   getTotal() {
      let total = 0;
      for (var i = 0; i < this.state.items.length; i++) {
         total += this.state.items[i].amount
      }
      return total;
   }
   static getDerivedStateFromProps(props, state) {
      let newState = {
         items: props.items
      }
      return newState;
   }
   render() {
      const lists = this.state.items.map((item) =>
         <tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
            <td>{item.name}</td>
            <td>{item.amount}</td>
            <td>{new Date(item.spendDate).toDateString()}</td>
            <td>{item.category}</td>
            <td><a href="#"
               onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
         </tr>
      );
      return (
         <div>
            <table onMouseOver={this.handleMouseOver}>
               <thead>
                  <tr>
                     <th>Item</th>
                     <th>Amount</th>
                     <th>Date</th>
                     <th>Category</th>
                     <th>Remove</th>
                  </tr>
               </thead>
               <tbody>
                  {lists}
                  <tr>
                     <td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
                     <td colSpan="4" style={{ textAlign: "left" }}>
                        {this.getTotal()}
                     </td>
                  </tr>
               </tbody>
            </table>
         </div>
      );
   }
}
export default ExpenseEntryItemList;

Next, open th index.js file and call the pager component and pass ExpenseEntryItemList as render props.

import React from 'react';
import ReactDOM from 'react-dom';
import Pager from './components/Pager'
import ExpenseEntryItemList from './components/ExpenseEntryItemList'

const items = [
   { id: 1, name: "Pizza", amount: 80, spendDate: "2020-10-10", category: "Food" },
   { id: 2, name: "Grape Juice", amount: 30, spendDate: "2020-10-12", category: "Food" },
   { id: 3, name: "Cinema", amount: 210, spendDate: "2020-10-16", category: "Entertainment" },
   { id: 4, name: "Java Programming book", amount: 242, spendDate: "2020-10-15", category: "Academic" },
   { id: 5, name: "Mango Juice", amount: 35, spendDate: "2020-10-16", category: "Food" },
   { id: 6, name: "Dress", amount: 2000, spendDate: "2020-10-25", category: "Cloth" },
   { id: 7, name: "Tour", amount: 2555, spendDate: "2020-10-29", category: "Entertainment" },
   { id: 8, name: "Meals", amount: 300, spendDate: "2020-10-30", category: "Food" },
   { id: 9, name: "Mobile", amount: 3500, spendDate: "2020-11-02", category: "Gadgets" },
   { id: 10, name: "Exam Fees", amount: 1245, spendDate: "2020-11-04", category: "Academic" }
]
const pageCount = 3;
ReactDOM.render(
   <React.StrictMode>
      <Pager
         items={items}
         pageCount={pageCount}
         render={
            pagerState => (
               <div>
                  <ExpenseEntryItemList items={pagerState.items} 
                        onDelete={pagerState.deleteHandler} />
               </div>
            )
         }
      />
   </React.StrictMode>,
   document.getElementById('root')
);

As we saw in the above example, the render component accepts any render props and it only calculates the pagination logic.

Next, serve the application making use of npm command.

npm start

Next, open the web browser and then enter http://localhost:3000 in the address bar and press enter.

pagination.logic


Alright guys! This is where we are going to be rounding up for this tutorial. In our next tutorial, we will be studying about ReactJS Material UI.

Feel free to ask your questions where necessary and we will attend to them as soon as possible. If this tutorial was helpful to you, you can use the share button to share this tutorial.

Follow us on our various social media platforms to stay updated with our latest tutorials. You can also subscribe to our newsletter in order to get our tutorials delivered directly to your emails.

Thanks for reading and bye for now.

Post a Comment

Hello dear readers! Please kindly try your best to make sure your comments comply with our comment policy guidelines. You can visit our comment policy page to view these guidelines which are clearly stated. Thank you.
© 2022 ‧ WebDesignTutorialz. All rights reserved. Developed by Jago Desain