How to properly update users count using Reactjs - reactjs

I have searched StackOverflow but most of the solutions found could not solve my issue at the moment.
The Code below displays users records from the array via reactjs.
Now I have the task to display users count on content display.
In Jquery/Javascript, I can achieve it using the function below
function updateCount(userId){
alert('userId: ' +userId);
var count = (userCount[userId] == undefined) ? 1 : userCount[userId] + 1;
userCount[userId] = count;
alert('Counter: ' +userCount[userId]);
console.log('Counter: ' +userCount[userId]);
$('#' + userId + ' label.userCount').html(count);
$('#' + userId + ' label.userCount').show();
}
Now in Reactjs, I created the following function below which I added to the main code but it throws error
Reactjs functions
updateCount = (userId) => {
alert('userId: ' +userId);
var count = (userCount[userId] == undefined) ? 1 : userCount[userId] + 1;
userCount[userId] = count;
alert('Counter: ' +userCount[userId]);
console.log('Counter: ' +userCount[userId]);
this.setState(state => ({
data: userCount[userId]
}));
}
Here is the error it shows
bundle.js:109480 Uncaught ReferenceError: userCount is not defined
at App._this.updateCount
Below is the entire code so far
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
userCount: []
};
}
componentDidMount() {
var userId = "100";
//var userCount = [];
//this.userCount = [];
this.setState({
data: [
{
id: "100",
name: "Luke"
},
{
id: "200",
name: "Henry"
},
{
id: "300",
name: "Mark"
}
]
});
this.updateCount = this.updateCount.bind(this);
}
updateCount = userId => {
alert("userId: " + userId);
var count = userCount[userId] == undefined ? 1 : userCount[userId] + 1;
userCount[userId] = count;
alert("Counter: " + userCount[userId]);
console.log("Counter: " + userCount[userId]);
this.setState(state => ({
data: userCount[userId]
}));
};
render() {
return (
<span>
<label>
<ul>
{this.state.data.map((person, i) => {
if (person.id == 100) {
this.updateCount(person.id);
return (
<div key={i}>
<div>
{" "}
{person.id}: {person.name}{" "}
</div>{" "}
</div>
);
} else {
this.updateCount(person.id);
}
})}
</ul>{" "}
</label>{" "}
</span>
);
}
}
Updated Section
It shows error of unexpected token with the code below
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
userCount: [],
};
}
componentDidMount() {
// componentWillMount(){
var userId= '100';
//var userCount = [];
//this.userCount = [];
this.setState({
data: [
{ id: "100", name: "Luke"},
{ id: "200", name: "Henry"},
{ id: "300", name: "Mark" }
]
});
this.updateCount = this.updateCount.bind(this);
}
updateCount = (userId) => {
//alert('userId: ' +userId);
const {userCount} = this.state;
var count = userCount[userId] == undefined ? 1 : userCount[userId] + 1;
//var count = (userCount[userId] == undefined) ? 1 : userCount[userId] + 1;
userCount[userId] = count;
alert('Counter kk000000me: ' +userCount[userId]);
console.log('Counter: ' +userCount[userId]);
this.setState({data11: count});
}
const result = this.state.data.map((person, i) => {
if (person.id == 100) {
this.updateCount(person.id); //Every time this is called, it triggers setState
return (
<div key={i}>
<div>
{" "}
{person.id}: {person.name}{" "}
</div>{" "}
</div>
);
} else {
this.updateCount(person.id);
}
});
render() {
return (
<span>
<label>
<ul> {result} </ul>
</label>
</span>
);
}

To access your state variables, you need to do it like
this.state.userCount
In your example you're trying to access your userCount without referring to it in the state.
var count = userCount[userId] == undefined ? 1 : userCount[userId] + 1;
You should instead do:
const {userCount} = this.state;
var count = userCount[userId] == undefined ? 1 : userCount[userId] + 1;
Edit
Warning: Cannot update during an existing state transition (such as within render or another component's constructor)
To fix this issue, you have to ensure that your component is not triggering setState inside of itself. In your snippet, when you map over your data, it calls updateCount which then calls this.setState() which will trigger a re-render.
One solution would be to assign the result of your map to a const, and the render it.
const result = this.state.data.map((person, i) => {
if (person.id == 100) {
this.updateCount(person.id); //Every time this is called, it triggers setState
return (
<div key={i}>
<div>
{" "}
{person.id}: {person.name}{" "}
</div>{" "}
</div>
);
} else {
this.updateCount(person.id);
}
});
...
render() {
return (
<span>
<label>
<ul> {result} </ul>
</label>
</span>
);
}

Related

Cancel to retain previous state is not working

I have apply and cancel button with list of checkeboxes.
Once apply is clicked the selected/checked value should get stored in state prevVal variable.
On seleting some other check box and clicking cancel should make checkbox to get populated with prevVal
But on clicking cancel currentValue is getting populated.
I am using a temporary variable to hold the current state on handle checkbox event.
After I push value to a temporary variable my state is getting auto updated with the temporary value.
Could anyone help me with this?
I think this is what youre looking for :)
Also here is the sandbox if you want to see the functionality: https://stackblitz.com/edit/react-pmdgir?file=index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
prevVal: [],
currentVal: [],
filter: [
{
key: 'a'
},
{
key: 'b'
},
{
key: 'c'
}
]
}
}
toggleCurrentVal = (key) => {
const currentVal = this.state.currentVal
const beforeSlicedVal = []
if(currentVal.includes(key)){
currentVal.splice(currentVal.indexOf(key), 1)
} else {
currentVal.push(key)
}
this.setState((prevState) => {
const prevStateValue = prevState.prevVal.length <= currentVal.length ? prevState.currentVal.filter((val) => val !== key) : prevState.prevVal
return {
currentVal: currentVal.sort(),
prevVal: prevStateValue
}
}, () => console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal ))
}
applyFilter = () => {
console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal )
}
cancel = () => {
this.setState({
currentVal: [],
prevVal: this.state.currentVal
}, () => console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal ))
}
render() {
let checkboxes = [];
let checkedValues = this.state.currentVal
if (this.state.filter) {
this.state.filter.map(data => {
checkboxes.push(
<div><input type="checkbox" checked={checkedValues.includes(data.key)}name="a" value={data.key} onClick={() => this.toggleCurrentVal(data.key)}></input></div>
)
})
}
return (
<div>
{checkboxes}
<button onClick={this.applyFilter}>Apply</button>
<button onClick={this.cancel}>Cancel</button>
</div>)
}
}
render(<App />, document.getElementById('root'));

Why the UI updated just after refresh page in ReactJs

I'm new in ReactJs and I got stuck in this issue and couldn't find any solution by browsing. Please let me know if this has been answered somewhere else.
My first problem is how can I update the component on the same page in React. I thought it should happen automatically when the state is changed but in my case, it doesn't.
What I'm trying to do is API call after onClick on div and shows the Child list instead of the current list in a specific part of the page (component) and this process continues up to end of the parent/child list. but when I click the div, a value of Localstorage is updated, the API successfully called and everything goes to the right place except showing the result on the page. Once I refresh the page the updated list shows well.
I thought maybe it's because I handle onclick in Childlist component but when I move it to the main component nothing changed.
Also, I would be grateful if you have any tips for coding better.
this is my code :
Parent component :
export class MainP5 extends React.Component {
constructor(props) {
super(props);
this.handleNextBtn = this.handleNextBtn.bind(this);
this.handleBackBtn = this.handleBackBtn.bind(this);
this.childItemHandler = this.childItemHandler.bind(this);
this.state = {
Items: '',
doRedirect: false,
doRedirectBack: false,
doNothing: false
};
}
componentDidMount() {
let selectedItemID = localStorage.getItem('SelectedTemplateID');
let itemIdList = "[\"" + localStorage.getItem('SelectedSectionsChildID') + "\"]";
request
.post(conn.GetItems)
.query({templateId: selectedItemID})
.send(itemIdList)
.set('Authorization', `Bearer ${localStorage.getItem("tokenId")}`)
.set('Content-Type', 'application/json-patch+json')
.set('accept', 'application/json')
.set('dataType', 'jsonp')
.end((err, res) => {
this.setState({
Items: res != null ? res.text : ""
});
});
}
childItemHandler(selectedID) {
alert(selectedID);
this.setState({
doNothing: true
});
localStorage.setItem('SelectedSectionsChildID', selectedID);
}
handleNextBtn(e) {
this.setState({
doRedirect: true
});
}
handleBackBtn(e) {
this.setState({
doRedirectBack: true
});
}
render() {
let {doRedirect, doRedirectBack, Items} = this.state;
const FixedListFields = JSON.parse(("[" + (Items.substring(1, Items.length - 1)) + "]"));
console.log("===> " + FixedListFields);
if (doRedirect) {
return (<Redirect to={`/assessment/page6?id_token=${localStorage.getItem("tokenId")}`}/>)
} else if (doRedirectBack) {
return (<Redirect to={`/assessment/page4?id_token=${localStorage.getItem("tokenId")}`}/>)
} else {
return (
<div>
<Grid>
<Row style={{height: '100vh'}}>
{/*---------------------- Right Section --------------------------------*/}
<Col sm={8}>
{FixedListFields.map((item, i) => {
return <div key={i}>
<Row className="margin_h">
<Col sm={12}>
<h3 id="header1">{item.label}</h3>
</Col>
</Row>
<Childlist childItemHandler={this.childItemHandler} key={item.childIds} childIds={item.childIds}/>
</div>
})}
</Col>
</Row>
</Grid>
</div>
);
}
}
}
Childlist Component :
class Childlist extends React.Component {
constructor(props) {
super(props);
this.checkBoxHandler = this.checkBoxHandler.bind(this);
this.childItemHandler = this.childItemHandler.bind(this);
this.state = {
ChildItems: '',
selectedCheckboxes: true,
selectedSectionIds: [],
redirectToChildItemList: false,
};
}
componentWillMount = () => {
this.selectedCheckboxes = new Set();
};
componentDidMount() {
let selectedItemID = localStorage.getItem('SelectedTemplateID');
let itemIdList = (this.props.childIds + '').split(",");
request
.post(conn.GetItems)
.query({templateId: selectedItemID})
.send(itemIdList)
.set('Authorization', `Bearer ${localStorage.getItem("tokenId")}`)
.set('Content-Type', 'application/json-patch+json')
.set('accept', 'application/json')
.end((err, res) => {
this.setState({
ChildItems: res != null ? res.text : ""
});
});
}
childItemHandler(selectedID) {
localStorage.setItem('SelectedSectionsChildID', selectedID);
}
render() {
if (this.state.redirectToChildItemList) {
return (<Redirect to={`./page5?id_token=${localStorage.getItem("tokenId")}`}/>)
} else {
let {ChildItems} = this.state;
const FixedChildListFields = JSON.parse(("[" + (ChildItems.substring(1, ChildItems.length - 1)) + "]"));
const temp = this.state.selectedSectionIds + '';
let tempArray = temp.split(",");
return FixedChildListFields.map((childItem, i) => {
return <div>
<Row className="margin_m">
<Col sm={11}>
<div>
<div onClick={ () => this.props.childItemHandler(childItem.id) }
className="list_block" key={i}>
<span>{childItem.label} : </span>
<span>{childItem.description} </span>
{(childItem.childIds) ? (<img className=" align_vertical align_right"
src={arrow}
alt={'img'} width="40px" height="40px"/>) : ""
}
</div>
</div>
</Col>
</Row>
</div>
}
)
}
}
}

Showing two different components based on return value in react js

I have search function where on entering text it returns the object from an array(json data) and based on the condition (whether it as an object or not) I need to show two different components ie. the list with matched fields and "No matched results found" component.
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
searchTextData: '',
isSearchText: false,
isSearchOpen: false,
placeholderText:'Search Content',
filteredMockData: [],
dataArray: []
};
}
handleSearchChange = (event, newVal) => {
this.setState({ searchTextData: newVal })
if (newVal == '') {
this.setState({ clearsearch: true });
this.setState({
filteredMockData: []
});
this.props.onDisplayCloseIcon(true);
} else {
this.props.onDisplayCloseIcon(false);
searchData.searchResults.forEach((item, index, array) => {
this.state.dataArray.push(item);
});
this.setState({ filteredMockData: this.state.dataArray });
}
}
clearInput = () => {
this.setState({ searchTextData: '' })
}
isSearchText = () => {
this.setState({ isSearchText: !this.state.isSearchText });
}
onSearchClick = () => {
this.setState({ isSearchOpen: !this.state.isSearchOpen });
this.setState({ searchTextData: '' });
this.props.onDisplayCloseIcon(true);
}
renderSearchData = () => {
const SearchDatasRender = this.state.dataArray.map((key) => {
const SearchDataRender = key.matchedFields.pagetitle;
return (<ResultComponent results={ SearchDataRender } /> );
})
return SearchDatasRender;
}
renderUndefined = () => {
return ( <div className = "search_no_results" >
<p> No Recent Searches found. </p>
<p> You can search by word or phrase, glossary term, chapter or section.</p>
</div>
);
}
render() {
return ( <span>
<SearchIcon searchClick = { this.onSearchClick } />
{this.state.isSearchOpen &&
<div className = 'SearchinputBar' >
<input
placeholder={this.state.placeholderText}
className= 'SearchInputContent'
value = { this.state.searchTextData}
onChange = { this.handleSearchChange }
/>
</div>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<span className='clearText'>
<ClearIcon className='clearIcon' clearClick = { this.clearInput }/>
</span>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<div className="SearchContainerWrapper">
<div className = "arrow-up"> </div>
<div className = 'search_result_Container' >
<div className = "search_results_title" > <span> Chapters </span><hr></hr> </div>
<div className="search_show_text" >
<ul className ="SearchScrollbar">
{this.state.filteredMockData.length ? this.renderSearchData() : this.renderUndefined() }
</ul>
</div>
</div>
</div>}
</span>
);
}
}
Search.propTypes = {
intl: intlShape.isRequired,
onSearchClick: PropTypes.func,
isSearchBarOpen: PropTypes.func,
clearInput: PropTypes.func,
isSearchText: PropTypes.func
};
export default injectIntl(Search);
Search is my parent component and based on the matched values I need to show a resultComponent like
class ResultComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render(){
console.log(this.props.renderSearchData(),'Helloooo')
return(<p>{this.props.renderSearchData()}</p>)
}
}
ResultComponent.propTypes = {
results: PropTypes.string.isRequired
};
I'm getting an error "renderSearchData is not an function".I'm new to react and Hope someone can help.
The only prop passed to ResultComponent component is results
So in ResultComponent Component Replace
this.props.renderSearchData()
With
this.props.results

Cannot setState() on dynamically generated components

Let keyComponent filters a string for keywords and return them with an event handler(to toggle them) and generates a state (targetState) within this.state. The problem is that if I click on any of the keywords the state isn't updated/changed. I can see all the states being generated in this.state through console.log. they are simply not updating when clicked, no errors either.
I would appreciate some help ;-)
import React from 'react';
import { render } from 'react-dom';
import { sectionUpsert } from '/imports/api/userProgress/upsertMethods.jsx';
export default class LetsCheck extends React.Component {
constructor(props) {
super(props);
this.state = {
reference: props.reference,
text: props.text,
keys: props.keys,
CorrectArray: [],
};
}
handleClick(e, TxtBit, targetState) {
console.log(targetState);
console.log(this.state.targetState);
let tempA = this.state.CorrectArray;
let target = targetState;
tempA.push(TxtBit);
let obj = { [target]: true, }
console.log(obj);
this.setState(obj);
// this.setState({
// CorrectArray: tempA,
// [target]: true,
// });
console.log(this.state);
}
handleUnclick(e, TxtBit, targetState) {
console.log('unclicked' + TxtBit + index);
}
componentWillUnmount() {
let keys = this.state.keys;
let correct = this.state.CorrectArray;
let keyWW = keys.filter(function(key){
return !correct.includes(key) && keys.indexOf(key) % 2 === 0
});
const secData = {
userId: Meteor.userId(),
ref: this.state.reference,
keyWR: this.state.CorrectArray,
keyWW: keyWW,
porSect: Math.round((this.state.CorrectArray.length / (keyWW.length + this.state.CorrectArray.length)) * 100),
};
sectionUpsert.call(secData);
}
render() {
let keys = this.state.keys;
let clicked = this.state;
let filter = keys.filter( function(key) {
return keys.indexOf(key) % 2 === 0;
});
let KeyComponent = this.state.text.map(function(TxtBit, index) {
let match = false;
let checkMatch = function(TxtBit, filter) {
for (var y = filter.length - 1; y >= 0; y--) {
if ( TxtBit == filter[y] ) {
match = true
}
}
};
checkMatch(TxtBit, filter);
if( match ) {
targetState = 'KeyBtn' + index;
clicked[targetState] = false;
return <a href="#" key={ index } style={{ display: `inline` }} onClick={ this.state[index] ? () => this.handleUnclick(this, TxtBit, targetState) : () => this.handleClick(this, TxtBit, targetState) } name={ TxtBit } className={ this.state[index] ? 'clicked': 'unclicked' } > { " "+TxtBit+ " " }</a>;
} else {
return <div key={ index } style={{ display: `inline` }} className="TxtBit"> { " "+TxtBit+ " " }</div>;
}
}.bind(this));
console.log(this.state);
return(
<div className="content">
<div className="content-padded">
<div> {KeyComponent}</div>
<p> { this.state.CorrectArray.length } / { this.state.keys.length / 2 } </p>
</div>
</div>
);
}
};
Try to bind them:
this.handleUnclick(this, TxtBit, targetState).bind(this)
or use arrow functions on handlers...
example: https://blog.josequinto.com/2016/12/07/react-use-es6-arrow-functions-in-classes-to-avoid-binding-your-methods-with-the-current-this-object/
Regards!

How to implement pagination in React

I am new to ReactJS and am creating a simple TODO application in it. Actually, it is a very basic app with no DB connection, tasks are stored in an array. I added Edit and Delete functionality to it now I want to add pagination.
How do I implement it? Any help will be appreciated. Thank you...!!
I've implemented pagination in pure React JS recently. Here is a working demo: http://codepen.io/PiotrBerebecki/pen/pEYPbY
You would of course have to adjust the logic and the way page numbers are displayed so that it meets your requirements.
Full code:
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')
);
I have tried to recreate the simple pagination example given by piotr-berebecki which was great. But when there will be a lot of pages then the pagination will overflow in the screen. So, I used previous and back button along with forward and backward button to stream back and forth between the pages. And for design part I have used bootstrap 3.
You can customize no of pages to display in pagination using the pagebound values. Make sure to use same value for upperPageBound and 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')
);
Working demo link : https://codepen.io/mhmanandhar/pen/oEWBqx
Image : simple react pagination
Here is a way to create your Custom Pagination Component from react-bootstrap lib and this component you can use Throughout your project
Your Pagination Component (pagination.jsx or 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
};
Use of Above Child Component in your Parent Component as shown below
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>
);
}
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')
);
I recently created this Pagination component that implements paging logic like Google's search results:
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;
And here's an example App component that uses the Pagination component to paginate a list of 150 example items:
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>
                        JasonWatmore.com
                    </p>
                </div>
            </div>
        );
    }
}
 
export default App;
For more details and a live demo you can check out this post
Please see this code sample in codesandbox
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>
);
}
1- Use react-bootstrap
import React from "react";
import { Pagination } from "react-bootstrap";
import Link from "next/link";
interface PaginateProps {
pages: number;
page: number;
keyword?: string;
isAdmin?: boolean;
}
const Paginate = ({
pages,
page,
// keyword is for searching
keyword = "",
isAdmin = false,
}: PaginateProps) => {
return pages > 1 ? (
<Pagination>
{/* keys returns a new Array iterator contians the keys for each index */}
// pages is total number of pages
{[...Array(pages).keys()].map((x) => (
// this is next.js component. you could use react Link
<Link
passHref
key={x + 1}
href={
!isAdmin
// since array indexing starts at 0, use x+1
? `/?keyword=${keyword}&page=${x + 1}`
: `/admin/productlist/?keyword=${keyword}&page=${x + 1}`
}
>
<Pagination.Item active={x + 1 === page}>{x + 1}</Pagination.Item>
</Link>
))}
</Pagination>
) : null;
};
export default Paginate;
then use it in a page.
<Paginate page={page} pages={pages} keyword={keyword} />
2- use react-js pagination
import Pagination from "react-js-pagination";
// bootstrap class to center
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={page}
itemsCountPerPage={resPerPageValue}
totalItemsCount={Count}
onChange={handlePagination}
nextPageText={"Next"}
prevPageText={"Prev"}
firstPageText={"First"}
lastPageText={"Last"}
// overwriting the style
itemClass="page-item"
linkClass="page-link"
/>
</div>
onChange handler
const handlePagination = (pageNumber) => {
// window.location.href will reload entire page
router.push(`/?page=${pageNumber}`);
};
I've recently created library which helps to cope with pagination cases like:
storing normalized data in Redux
caching pages based on search filters
simplified react-virtualized list usage
refreshing results in background
storing last visited page and used filters
DEMO page implements all above features.
Source code you can find on Github
A ReactJS dumb component to render pagination. You can also use this Library:
https://www.npmjs.com/package/react-js-pagination
Some Examples are Here:
http://vayser.github.io/react-js-pagination/
OR
You can also Use this https://codepen.io/PiotrBerebecki/pen/pEYPbY
Make sure you make it as a separate component
I have used 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;
I have implemented pagination + search in ReactJs, see the output:
Pagination in React
View complete code on GitHub: https://github.com/navanathjadhav/generic-pagination
Also visit this article for step by step implementation of pagination: https://everblogs.com/react/3-simple-steps-to-add-pagination-in-react/
Give you a pagination component, which is maybe a little difficult to understand for newbie to react:
https://www.npmjs.com/package/pagination

Resources