I am learning React js and this might sound like a newbie question. I am trying to implement a paginate function in react js from an API data. I am not sure how to implement the logic though. I have passed on props like the page size, current page and the data that needs to be rendered. Here is my code:
App.js
import React, { Component } from 'react'
import Result from './Result';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Navbar from 'react-bootstrap/Navbar'
import Card from './Card';
import Loading from './Loading'
import Paginate from './Paginate';
export default class App extends Component {
constructor(){
super();
this.state = {
data: [],
totalData:[],
searchText:'',
searchResult:[],
isSearch:false,
isLoading:true,
pageSize:15,
currentPage:1
}
this.onSearchChange=this.onSearchChange.bind(this);
this.handlePageChange=this.handlePageChange.bind(this);
}
onSearchChange= (e) =>{
console.log("search change "+this.state.searchText)
this.setState({
searchText:e.target.value,
isSearch:true
})
console.log("api data"+this.state.data[0])
}
/* fetchSearchResult= () =>{
console.log(this.state.searchText)
console.log("inside fetch")
let store= this.state.data.map(item=>{
let {country}=item
return(country)
})
console.log(store)
var areEqual = store.includes(this.state.searchText);
console.log(this.state.areEqual)
return (areEqual)?
store:'not matched'
// return store;
} */
componentDidMount() {
const url =
'https://corona.lmao.ninja/countries?sort=country'
fetch(url)
.then(result => result.json())
.then(result => {
this.setState({
data: result.reverse(),
isLoading:false
})
})
const totalUrl =
'https://corona.lmao.ninja/all'
fetch(totalUrl)
.then(result => result.json())
.then(result => {
//let store=result;
//console.log("store data"+store)
this.setState({
totalData: result
})
console.log("2nd fetched data"+this.state.totalData)
})
}
handlePageChange= (page) =>{
this.setState({
currentPage:page
})
}
render() {
return (
this.state.isLoading?<Loading/>:
<div id="main">
<Navbar bg="dark" variant="dark">
<Navbar.Brand href="#home">
<Button id="live_text"
>Live</Button>
<img
alt=""
src="/logo.svg"
width="100"
height="30"
className="d-inline-block align-top"
/>{' '}
Covid-19 dashboard
{this.state.curTime}
</Navbar.Brand>
</Navbar>
<Form.Group>
<Form.Label>Search</Form.Label>
<Form.Control value={this.state.searchText}onChange={this.onSearchChange} type="text" placeholder="Enter country" />
</Form.Group>
<Card totalData={this.state.totalData}/>
<Paginate
dataCount={this.state.data.length}
pageSize={this.state.pageSize}
onPageChange={this.handlePageChange}
currentPage={this.state.currentPage}/>
<Result data={this.state.data}
toSearch={this.state.searchText}
searchCheck={this.state.isSearch}
searchValue={this.state.searchText}/>
</div>
)
}
}
Paginate.js
import React from 'react'
import _ from 'lodash'
export default function Paginate(props) {
const {pageSize, dataCount, onPageChange, currentPage}=props;
console.log("current page"+currentPage)
const pagesCount=Math.ceil(dataCount/pageSize);
const pages=_.range(1,pagesCount+1);
return (
<div>
<nav aria-label="...">
<ul class="pagination">
{pages.map((page)=>{
return(
<li key={page}class={(page===currentPage)?"page-item active":"page-item"}>
<a class="page-link" href="#" onClick={()=>onPageChange(page)}>{page}</a>
</li>
)
})}
</ul>
</nav>
</div>
)
}
Result.js
import React from 'react'
import Table from 'react-bootstrap/Table';
const Result = (props) => {
console.log('props value is:'+props.data)
let {searchCheck, searchValue}=props;
let update=props.data.map((item)=>{
const { countryInfo, country, cases, deaths, recovered, active, casesPerOneMillion} = item;
return(
(searchCheck)?country.toUpperCase().includes(searchValue.toUpperCase())?
<tbody>
<tr key={countryInfo._id}>
<td><img style={{height:'25px',width:'50px'}}src={countryInfo.flag}/></td>
<td>{country}</td>
<td>{cases}</td>
<td>{active}</td>
<td>{recovered}</td>
<th>{casesPerOneMillion}</th>
<td>{deaths}</td>
</tr>
</tbody>:
'':
<tbody>
<tr key={countryInfo._id}>
<td><img style={{height:'25px',width:'50px'}}src={countryInfo.flag}/></td>
<td>{country}</td>
<td>{cases}</td>
<td>{active}</td>
<td>{recovered}</td>
<th>{casesPerOneMillion}</th>
<td>{deaths}</td>
</tr>
</tbody>
)
})
return (
<div>
<Table striped bordered hover variant="dark">
<thead>
<tr>
<th>Flag</th>
<th>Country</th>
<th>Cases</th>
<th>Active</th>
<th>Recovered</th>
<th>Cases per one Million</th>
<th>Deaths</th>
</tr>
</thead>
{update}
</Table>
</div>
)
}
export default Result;
Codesandbox live
In my solution, I am fetching the required records from data based on page no and storing them in a new array( as a state) and passing them to Result.js
Tips: Use the key in Result.js at the top level of children in Result.js you are using the key at the tag instead of that you need to you it on
App.js
import React, { Component } from "react";
import Result from "./Result";
import Paginate from "./Paginate";
export default class App extends Component {
constructor() {
super();
this.state = {
data: [],
totalData: [],
searchText: "",
searchResult: [],
isSearch: false,
isLoading: true,
pageSize: 15,
currentPage: 1,
dataToShow: []
};
this.onSearchChange = this.onSearchChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
}
onSearchChange = e => {
this.setState({
searchText: e.target.value,
isSearch: e.target.value === "" ? false : true
});
};
/* fetchSearchResult= () =>{
console.log(this.state.searchText)
console.log("inside fetch")
let store= this.state.data.map(item=>{
let {country}=item
return(country)
})
console.log(store)
var areEqual = store.includes(this.state.searchText);
console.log(this.state.areEqual)
return (areEqual)?
store:'not matched'
// return store;
} */
componentDidMount() {
const url = "https://corona.lmao.ninja/countries?sort=country";
fetch(url)
.then(result => result.json())
.then(result => {
this.setState({
data: result.reverse(),
dataToShow: result.slice(0, 15),
isLoading: false
});
});
const totalUrl = "https://corona.lmao.ninja/all";
fetch(totalUrl)
.then(result => result.json())
.then(result => {
// let store=result;
// console.log("store data"+store)
this.setState({
totalData: result
});
});
}
handlePageChange = page => {
const { data, pageSize } = this.state;
this.setState({
currentPage: page,
dataToShow: data.slice(pageSize * (page - 1), pageSize * (page - 1) + 15)
});
};
render() {
const {
searchText,
data,
pageSize,
currentPage,
isSearch,
dataToShow
} = this.state;
return (
<div id="main">
<input
value={searchText}
onChange={this.onSearchChange}
type="text"
placeholder="Enter country"
/>
<Paginate
dataCount={data.length}
pageSize={pageSize}
onPageChange={this.handlePageChange}
currentPage={currentPage}
/>
<Result
data={isSearch ? data : dataToShow}
toSearch={searchText}
searchCheck={isSearch}
searchValue={searchText}
/>
</div>
);
}
}
Paginate.js
import React from "react";
export default function Paginate(props) {
const { pageSize, dataCount, onPageChange } = props;
const pagesCount = Math.ceil(dataCount / pageSize);
const Pages = new Array(pagesCount).fill(0);
// const Pages = [1,2,3,5]
return (
<div>
<nav aria-label="...">
{Pages.map((element, index) => (
<button
key={index}
type="button"
onClick={() => onPageChange(index + 1)}
>
{index + 1}
</button>
))}
</nav>
</div>
);
}
Result.js
import React from "react";
const Result = props => {
const { searchCheck, searchValue } = props;
const update = props.data.map(item => {
const {
countryInfo,
country,
cases,
deaths,
recovered,
active,
casesPerOneMillion
} = item;
return searchCheck ? (
country.toUpperCase().includes(searchValue.toUpperCase()) ? (
<tbody key={countryInfo._id}>
<tr>
<td>
<img
style={{ height: "25px", width: "50px" }}
src={countryInfo.flag}
/>
</td>
<td>{country}</td>
<td>{cases}</td>
<td>{active}</td>
<td>{recovered}</td>
<th>{casesPerOneMillion}</th>
<td>{deaths}</td>
</tr>
</tbody>
) : (
""
)
) : (
<tbody key={countryInfo._id}>
<tr>
<td>
<img
style={{ height: "25px", width: "50px" }}
src={countryInfo.flag}
/>
</td>
<td>{country}</td>
<td>{cases}</td>
<td>{active}</td>
<td>{recovered}</td>
<th>{casesPerOneMillion}</th>
<td>{deaths}</td>
</tr>
</tbody>
);
});
return (
<div>
<table>
<thead>
<tr>
<th>Flag</th>
<th>Country</th>
<th>Cases</th>
<th>Active</th>
<th>Recovered</th>
<th>Cases per one Million</th>
<th>Deaths</th>
</tr>
</thead>
{update}
</table>
</div>
);
};
export default Result;
Related
I am trying to use reactstrap's Collapse to collapse two alternating sections.
import React, { Component, Fragment, Suspense } from "react";
import ReactDOM from 'react-dom';
import { PDFViewer } from '#react-pdf/renderer'
import NewWindow from 'react-new-window'
import Config from 'config';
import { Button, Collapse } from 'reactstrap';
import { formatter } from '../common.js'
import { format } from 'date-fns';
import Pagination from '../Pagination';
import "./Order.css";
import Modal from 'react-modal';
import PaymentModal from './paymentModal.js';
import Invoice from './Reports/Invoice';
Modal.setAppElement('#root');
let PageSize = 25;
class Portal extends React.Component {
constructor() {
super();
this.state = {
name: 'React',
apiData: [],
currentPage: 1,
currentTableData: [],
orderList: [],
isOpen: false,
pdfView: null,
viewInvoices: true,
viewOrders: false
};
}
showInvoices() {
console.log("Show Invoices Clicked")
this.setState({ viewInvoices: true });
this.setState({ viewOrders: false });
}
showOrders() {
console.log("Show Orders Clicked")
this.setState({ viewInvoices: false });
this.setState({ viewOrders: true });
}
async componentDidMount() {
console.log('app mounted');
const tokenString = sessionStorage.getItem("token");
const token = JSON.parse(tokenString);
let headers = new Headers({
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Bearer ' + token.token
});
const response = await fetch(Config.apiUrl + `/api/Orders/GetAllInvoices`, {
method: "GET",
headers: headers
});
const json = await response.json();
console.log(json);
this.setState({ orderList: json });
}
componentDidUpdate(_, prevState) {
console.log('Component Updated');
if (prevState.currentPage !== this.state.currentPage || prevState.orderList !== this.state.orderList) {
const firstPageIndex = (this.state.currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
this.setState({ currentTableData: this.state.orderList.slice(firstPageIndex, lastPageIndex) });
}
}
render() {
const orders = this.state.orderList;
const currentTableData = this.state.currentTableData;
const { isOpen } = this.state;
let onRequestClose = () => {
this.setState({ isOpen: false });
}
let handleClick = () => {
console.log("Clicked")
this.setState({ isOpen: true });
}
function handleInvoiceClick(e, invoice) {
e.preventDefault();
console.log(`invoice: ${JSON.stringify(invoice)}`)
if (this.state.pdfView === null) {
const headerString = sessionStorage.getItem("header");
const header = JSON.parse(headerString);
const buff = new Buffer(header.logoImage, 'base64');
let pdf = (
<PDFViewer width="1000" height="600" className="portal">
<Invoice invoice={invoice} buff={buff} />
</PDFViewer>
);
this.setState({ pdfView: pdf });
} else {
this.setState({ pdfView: null });
}
}
handleInvoiceClick = handleInvoiceClick.bind(this);
return (
<div id="bootstrap-overrides">
<h2>Portal</h2>
<div className="row">
<div className="block col-1">
<p><button onClick={this.showInvoices.bind(this)}>Invoices</button></p>
<p><button onClick={this.showOrders.bind(this)}>Orders</button></p>
</div>
<div className="block col-2">
<br />
{this.state.pdfView}
<Collapse isOpen={this.state.showInvoices}>
<h3>Open Invoices</h3>
<h4>A list of completed orders purchased under this account.</h4>
<table className="table table-striped table-bordered">
<thead>
<tr>
<th className="number">Invoice Number</th>
<th className="date">Invoice Date</th>
<th className="date">Due Date</th>
<th className="amount">Total</th>
<th className="customer">Ship To</th>
<th className="date">Actual Ship Date</th>
<th className="button"></th>
</tr>
</thead>
<tbody>
{currentTableData && currentTableData.map(order =>
<>
<tr key={order.sopnumbe}>
<td><a href="#" onClick={(e) => handleInvoiceClick(e, order)}>{order.sopnumbe}</a></td>
<td>{format(Date.parse(order.invodate), 'MM/dd/yyyy')}</td>
<td>{format(Date.parse(order.duedate), 'MM/dd/yyyy')}</td>
<td>{formatter.format(order.docamnt)}</td>
<td>{order.custname}</td>
<td>{format(Date.parse(order.actlship), 'MM/dd/yyyy')}</td>
<td><Button className="BtnPay" onClick={handleClick}>Pay</Button></td>
</tr>
{isOpen ? <PaymentModal invoice={order} onRequestClose={onRequestClose} /> : null}
</>
)}
</tbody>
</table>
<Pagination
className="pagination-bar"
currentPage={this.state.currentPage}
totalCount={orders.length}
pageSize={PageSize}
onPageChange={page => this.setState({ currentPage: page })}
/>
</Collapse>
<Collapse isOpen={this.state.showOrders}>
<h3>Open Orders</h3>
<h4>A list of completed orders purchased under this account.</h4>
</Collapse>
</div>
</div>
</div>
);
}
}
export default Portal;
The isOpen doesn't seem to be reading my state. The Portal loads with both sections collapsed. My two buttons run (I get a console log entry for both) but don't effect the display. I would think the initial Invoice should be open as well. Why does my reactstrap Collapse element not reflect my initial or changed state? Do I need to add anything to componentDidUpdate? Any tips or advice will be appreciated.
reactstrap = reactstrap#9.0.1
I think you are using the wrong variables
<Collapse isOpen={this.state.showInvoices}>
<Collapse isOpen={this.state.showOrders}>
should be
<Collapse isOpen={this.state.viewInvoices}>
<Collapse isOpen={this.state.viewOrders}>
The show.. ones are the functions. The isOpen expects a boolean value
I have this on the front end (react) right now.
import '../styles/TourPage.css';
import React, { Component } from 'react';
import axios from 'axios'
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: []
}
}
componentDidMount() {
axios.get('/getResults')
.then( res => {
console.log("Res is: ", res.data)
this.setState({
myData: res.data
});
})
console.log("res.data", this.state.myData)
}
render() {
console.log("res.data", this.state.myData)
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{this.state.myData.length > 0? this.state.myData.map((data, index) => (
<tr>
{/* <tr key={index}> */}
<td>{data.location}</td>
<td>{data.Services}</td>
<td>{data.cnum}</td>
<button onClick={this.click} disabled={this.state.isLoading}> Delete </button>
{/* {this.state.data} */}
{/* </tr> */}
</tr>
))
: "No Data Found"}
</table>
);
}
}
export default TourPage;
What I want to do, is on a button click, set data._id in the state, and then call Axios to post it to the Node.JS backend server, so I can update the database. Basically to delete the document. as you can see below, I tried with <a href> but that seems to be an HTML-specific thing. I also tried with the button, but I cannot figure it out. How can I do this?
I have refactored most of your code. You can pass id using an anonymous arrow function.
Do modify this to suit your needs.
import { render } from "react-dom";
import React, { Component } from "react";
import axios from "axios";
import "../styles/TourPage.css";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("/getResults")
.then((res) => {
this.setState({
myData: res.data
});
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
deleteById = (id) => {
// You'll get the id here
// Delete by id code goes here
};
render() {
// You can handle the loader part here with isLoading flag. In this case No data found will be shown initially and then the actual data
let { myData, isLoading } = this.state;
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{myData.length > 0
? myData.map(({ location, Services, cnum, _id }, index) => (
<tr key={index}>
<td>{location}</td>
<td>{Services}</td>
<td>{cnum}</td>
<button
onClick={() => this.deleteById(_id)}
disabled={isLoading}
>
Delete
</button>
</tr>
))
: "No Data Found"}
</table>
);
}
}
export default TourPage;
Can you try this ?
render() {
deleteDoc = (id) => {
await fetch('service_url', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: id} )
.then(async response => {
await response.json().then(data => {
console.log(data);
});
})
.catch(err => {
console.log(err)
})
}
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{this.state.myData.length > 0 ? this.state.myData.map((data, index) => (
<tr>
<td>{data.location}</td>
<td>{data.Services}</td>
<td>{data.cnum}</td>
<button onClick={this.deleteDoc(data._id)} disabled={this.state.isLoading}> Delete </button>
</tr>
))
: "No Data Found"}
</table>
);
}
I have been working with code that uses table,
working code
export class BoardList extends Component {
static propTypes = {
boards: PropTypes.array.isRequired,
getBoards: PropTypes.func.isRequired,
deleteBoard: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getBoards();
}
render(){
this.props.boards.sort((boardA, boardB) => {return boardA.id - boardB.id })
const sortRow = this.props.boards.map(board => {
return (
<tr key={board.id}>
<td>{board.id}</td>
<td>{board.author}</td>
<td>{board.title}</td>
<td>{board.created}</td>
<td>{board.updated}</td>
<td>
<button className="btn btn-danger btn-sm" onClick={this.props.deleteBoard.bind(this, board.id)}>
Delete
</button>
</td>
</tr>
)
})
return (
<Fragment>
<table className="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Author</th>
<th>Title</th>
<th>Created</th>
<th>Updated</th>
<th />
</tr>
</thead>
<tbody>
{this.props.boards.length > 0 && (sortRow)}
</tbody>
</table>
</Fragment>
)
}
}
const mapStateToProps = state => ({
boards: state.boards.boards
})
export default connect(mapStateToProps, {getBoards, deleteBoard})(BoardList)
However, if I use different element, it does not work.
export class BoardList extends Component {
constructor(props){
super(props)
this.createCard = this.createCard.bind(this)
}
static propTypes = {
boards: PropTypes.array.isRequired,
getBoards: PropTypes.func.isRequired,
deleteBoard: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getBoards();
}
createCard(board) {
return (
<div key={board.id}>
<div className="card text-white bg-primary mb-3" style="max-width:20rem">
<div className="card-header">{board.author}</div>
<div className="card-body">
<h4 className="card-title">{board.title}</h4>
<p className="card-text">{board.body}</p>
<img src={board.image} style="max-width:100px"/>
</div>
</div>
</div>
)
}
render() {
this.props.boards.sort((boardA, boardB) => { return boardA.id - boardB.id });
const cardBoard = this.props.boards.map(this.createCard);
return (
<Fragment>
<h2>Boards</h2>
{this.props.boards.length > 0 && (cardBoard)}
</Fragment>
)
}
const mapStateToProps = state => ({
boards: state.boards.boards
})
export default connect(mapStateToProps, {getBoards, deleteBoard})(BoardList)
I get Uncaught (in promise) TypeError: Cannot read property 'data' of undefined. That data is from the my redux action.
export const getBoards = () => (dispatch, getState) => {
axios
.get("api/boards/", tokenConfig(getState))
.then(res => {
dispatch({
type: GET_BOARDS,
payload: res.data
})
})
.catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
}
}
I think both methods map the props, so there shouldn't be any issue, but since I am new to React, I must be missing something here.
I am failing to delete the clients from the API, i expected to click on Onclick button and the data should be deleted from the database:
Using the onclick button, the data should be deleted:
import React, { useRef } from 'react'
import ReactToPrint from 'react-to-print'
import { Table, Button } from 'react-bootstrap'
const Hello = () => {
alert('Name, Description, Start Date, End Date, Validity, Status')
}
class clientview extends React.Component {
constructor (props) {
super(props)
this.state = {
error: null,
clients: []
}
this.deleteTask = this.deleteTask.bind(this)
}
componentDidMount () {
const url = 'http://localhost:3001/clients/sel_all'
fetch(url)
.then(res => res.json())
.then(
result => {
console.log(result)
this.setState({
clients: result.data
})
},
error => {
this.setState({ error })
}
)
}
deleteTask (id, url = 'http://localhost:3001/clients/delete') {
return fetch(url + '/' + id, { method: 'DELETE' }).then(response =>
response.json()
)
}
render () {
const { error, clients, props } = this.state
if (error) {
return <div> Error:{error.message}</div>
} else {
return (
<div>
<h2>All Clients</h2>
<Table>
<thead>
<tr>
<th>No</th>
<th>Client Name</th>
<th>Client Address</th>
<th>Comment</th>
</tr>
</thead>
<tbody>
{clients.map(client => (
<tr key={client.id}>
<td>{client.id}</td>
<td>{client.name}</td>
<td>{client.address}</td>
<td>{client.comment}</td>
<td>
<button onClick={Hello}>View Contract</button>
<button>
<ReactToPrint
trigger={() => <button>print</button>}
content={() => this.componentRef}
/>
</button>
<button
onClick={() => {
props.editRow(client)
}}
className='button'
>
Edit
</button>
</td>
<button
onClick={() => {
this.deleteTask.bind(this)
}}
>
Dele
</button>
</tr>
))}
</tbody>
</Table>
</div>
)
}
}
}
export default clientview
I have implement React app getting database from MongoDB with Express Server.
For Pagination function is working well but when I implement search function is working only when typing in the input box. If I delete the character, it should search again but it is still.
Could anybody please help to verify my code??
IssueList.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import 'whatwg-fetch';
import Pagination from '../components/Pagination';
import IssueAdd from '../components/IssueAdd';
class IssueList extends Component {
constructor(props) {
super(props);
this.state = {
issues: [],
pageOfItems: [],
};
this.createIssue = this.createIssue.bind(this);
this.onChangePage = this.onChangePage.bind(this);
this.filterList = this.filterList.bind(this);
}
componentDidMount() {
this.loadData();
}
loadData() {
fetch('/api/issues').then(response => {
if (response.ok) {
response.json().then(data => {
data.records.forEach(issue => {
issue.created = new Date(issue.created);
if (issue.completionDate) {
issue.completionDate = new Date(issue.completionDate);
}
});
this.setState({ issues: data.records });
});
} else {
response.json().then(error => {
alert(`Failed to fetch issues ${error.message}`);
});
}
}).catch(err => {
alert(`Error in fetching data from server: ${err}`);
});
}
onChangePage(pageOfItems) {
this.setState({ pageOfItems: pageOfItems });
}
filterList = (e) => {
var updatedList = this.state.issues;
updatedList = updatedList.filter((item) => {
return item.title.toLowerCase().search(e.target.value.toLowerCase()) !== -1;
});
this.setState({ issues: updatedList });
}
render() {
return (
<div>
<h1>Issue Tracker</h1>
<hr />
<div className="filter-list">
<form>
<fieldset className="form-group">
<legend>Search</legend>
<input
type="text"
className="form-control form-control-lg"
placeholder="Search"
onChange={this.filterList}
/>
</fieldset>
</form>
</div>
<div className="panel panel-default">
<table className="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Status</th>
<th>Owner</th>
<th>Created</th>
<th>Effort</th>
<th>Completion Date</th>
<th>Title</th>
</tr>
</thead>
<tbody>
{this.state.pageOfItems.map(issue => (
<tr key={issue._id}>
<td>{issue._id}</td>
<td>{issue.status}</td>
<td>{issue.owner}</td>
<td>{issue.created.toDateString()}</td>
<td>{issue.effort}</td>
<td>{issue.completionDate ? issue.completionDate.toDateString() : ''}</td>
<td>{issue.title}</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination
items={this.state.issues}
onChangePage={this.onChangePage}
/>
<hr />
<IssueAdd createIssue={this.createIssue} />
</div>
);
}
}
export default IssueList;
Edited
I've tried to add loadData() function to the filterList()
filterList = (e) => {
this.loadData();
var updatedList = this.state.issues;
updatedList = updatedList.filter((item) => {
return item.title.toLowerCase().search(e.target.value.toLowerCase()) !== -1;
});
this.setState({ issues: updatedList });
}
It can search but after that it goes back to the initial state (page 1).
you need to add the value parameter to your input in order to control it's value. This could be your issue. I updated this to include adding a holder in state that holds the unfiltered array.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import 'whatwg-fetch';
import Pagination from '../components/Pagination';
import IssueAdd from '../components/IssueAdd';
class IssueList extends Component {
constructor(props) {
super(props);
this.state = {
issues: [],
holder: [],
pageOfItems: [],
};
this.createIssue = this.createIssue.bind(this);
this.onChangePage = this.onChangePage.bind(this);
this.filterList = this.filterList.bind(this);
}
componentDidMount() {
this.loadData();
}
loadData() {
fetch('/api/issues').then(response => {
if (response.ok) {
response.json().then(data => {
data.records.forEach(issue => {
issue.created = new Date(issue.created);
if (issue.completionDate) {
issue.completionDate = new Date(issue.completionDate);
}
});
this.setState({ issues: data.records, holder: data.records });
});
} else {
response.json().then(error => {
alert(`Failed to fetch issues ${error.message}`);
});
}
}).catch(err => {
alert(`Error in fetching data from server: ${err}`);
});
}
onChangePage(pageOfItems) {
this.setState({ pageOfItems: pageOfItems });
}
filterList = (e) => {
let { value } = e.target
this.setState({ value }, () => {
//running this after setting the value in state because of async
var updatedList = this.state.holder;
updatedList = updatedList.filter((item) => {
return item.title.toLowerCase().search(this.state.value.toLowerCase()) !== -1;
});
this.setState({ issues: updatedList });
})
}
render() {
return (
<div>
<h1>Issue Tracker</h1>
<hr />
<div className="filter-list">
<form>
<fieldset className="form-group">
<legend>Search</legend>
<input
type="text"
className="form-control form-control-lg"
placeholder="Search"
value={this.state.value}
onChange={this.filterList}
/>
</fieldset>
</form>
</div>
<div className="panel panel-default">
<table className="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Status</th>
<th>Owner</th>
<th>Created</th>
<th>Effort</th>
<th>Completion Date</th>
<th>Title</th>
</tr>
</thead>
<tbody>
{this.state.pageOfItems.map(issue => (
<tr key={issue._id}>
<td>{issue._id}</td>
<td>{issue.status}</td>
<td>{issue.owner}</td>
<td>{issue.created.toDateString()}</td>
<td>{issue.effort}</td>
<td>{issue.completionDate ? issue.completionDate.toDateString() : ''}</td>
<td>{issue.title}</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination
items={this.state.issues}
onChangePage={this.onChangePage}
/>
<hr />
<IssueAdd createIssue={this.createIssue} />
</div>
);
}
}
export default IssueList;