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;
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;
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
Related
export const COLUMNS = [
{
Header : 'Total Cases',
accessor : 'totalcases',
},
{
Header : 'Active Cases',
accessor : 'activecases',
},
{
Header : 'Total Recovery',
accessor : 'recovery',
},
{
Header : 'Total Death',
accessor : 'deaths',
},
{
Header : 'New Cases',
accessor : 'newcases',
},
]
function Table({countryData}) {
const columns = useMemo(()=> COLUMNS, []);
const data = useMemo(()=> countryData, []);
const {
....
setGlobalFilter,
} = useTable({
columns,
data
}, useGlobalFilter, useSortBy);
const{globalFilter} = state;
return (
<>
<GlobalFilter filter={globalFilter} setFilter={setGlobalFilter}/>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
</>
)
}
export default Table
here I want to sort the entire table by default on the basis of new cases column and want toggle sort only on active cases and recovery. as Im new to react i dont know how to provide custom sort..............................................................................................................................................................................................................................................................
you can use :
columns.sort((a, b) => (a.accessor > b.accessor ) ? 1 : -1) More informision at this link: https://flaviocopes.com/how-to-sort-array-of-objects-by-property-javascript/
I am trying to add new row onclick of accordion i.e while expand using reactable, attached the expected result.
I have showed the table structured data using Tr and Td from reactable but however, not sure to add the new row.
onclick of the arrow the dynamic row should expand,I tried to do so but wasn't able to achieve that.
class PolicyDetails extends Component {
showPolicyOperation = (e,models) => {
e.preventDefault();
const {callbacks} = this.props
const activeClass = document.querySelectorAll('.fa-angle-up')
const currentTarget = e.currentTarget;
if(currentTarget.classList.contains('fa-angle-up')){
currentTarget.classList.remove('fa-angle-up');
currentTarget.classList.add('fa-angle-down');
}else{
currentTarget.classList.remove('fa-angle-down');
currentTarget.classList.add('fa-angle-up');
}
activeClass && activeClass.forEach(node => {
node.classList.remove('fa-angle-up');
node.classList.add('fa-angle-down');
})
callbacks.fetchPoliciesWithId(models.id)
}
getHeaders = () => {
let headers = ([
<Th key="0" column=""></Th>,
<Th key="1" column="id">Policy Id</Th>,
<Th key="2" column="serviceType">Service</Th>,
<Th key="3" column="name">Policy Name</Th>,
<Th key="4" column="description">Policy Description</Th>,
<Th key="5" column="policyLabel">Policy Label</Th>,
<Th key="6" column="policyType">Policy Type</Th>,
<Th key="7" column="operation">Operation</Th>,
<Th key="8" column="action">Actions</Th>
])
return headers;
}
pageChange = (page) => {
this.cPoliciesData.params.page = page - 1 || undefined;
this.props.callbacks.fetchPolicies();
}
getRows = (models, idx) => {
const state = this.props.options._vState
let rows = ([
<Td key="0" column="">
<i className="fa pointer fa-angle-down"
aria-hidden="true" key = {idx} onClick={e => {
state.isPolicySelected = !state.isPolicySelected;
this.showPolicyOperation(e,models)
}}></i></Td>,
<Td key="1" column="id">{<a>{models.id}</a>}</Td>,
<Td key="2" column="serviceType">{models.serviceType || "--"}</Td>,
<Td key="3" column="name">{models.name || "--"}</Td>,
<Td key="4" column="description">{models.description || "--"}</Td>,
<Td key="5" column="policyLabel">{"--"}</Td>,
<Td key="6" column="policyType">{models.serviceType == 'tag' && models.policyType == 0 ? "Tag Based" : POLICY_TYPE[models.policyType].label}</Td>,
<Td key="7" column="operation">{"--"}</Td>,
<Td key="8" column="action">{"--"}</Td>,
]);
let operation = state.isPolicySelected && <Tr className="special-row">
<Th column="name">
<strong className="name-header">First Name, Last Name</strong>
</Th>
<Th column="age">
<em className="age-header">Age, years</em>
</Th>
</Tr>
rows.push(operation)
return rows;
}
render() {
const {options , callbacks} = this.props;
const {cPoliciesData, _vState} = options
return (
<Row className="m-t-md">
{/* <Col md={12}> */}
<PanelBody>
<Table data={cPoliciesData}
tableAttr={{ className: "table table-hover" }}
getHeaders={this.getHeaders}
getRowData={this.getRows}
pagination={true}
pageChange={this.pageChange}
>
</Table>
</PanelBody>
{/* </Col> */}
</Row>
)
}
}
You just need to add in array . and then with
UseEffect(()=>{
},[options._vState])
I'm working on this react table sorting when the user clicks on table header it needs to sort the table, sorting is working but the problem is I'm receiving new data every second through SignalR hub and it sets state udata to new data. When a user clicks on table header it sorts the table but again goes back to the new state changed by new data. And cancells the sorted table back to unsorted.
Is there any way I can keep sorted state and still receive data?
I'm new to react any help would be appreciated
constructor() {
super()
this.state = {
udata: [],
sort: {
column: null,
direction: 'desc',
},
}
}
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl('/signalserver')
.build()
connection
.start()
.then(function() {})
.catch(function(err) {
return console.error(err.toString())
})
connection.on(
'APIChannel',
function(data) {
this.setState({udata: data})
}.bind(this),
)
async function start() {
try {
await connection.start()
console.log('connected')
} catch (err) {
console.log(err)
setTimeout(() => start(), 5000)
}
}
connection.onclose(async () => {
await start()
})
}
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.state.udata.sort((a, b) => {
if (column === 'appName') {
// This sorts strings taking into consideration numbers in strings.
// e.g., Account 1, Account 2, Account 10. Normal sorting would sort it Account 1, Account 10, Account 2.
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
sortedData.reverse()
}
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
renderItem(item, key) {
const itemRows = [
<tr onClick={clickCallback} key={'row-data-' + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
'fa fa-circle text-c-' +
(item.appState === 'STARTED' ? 'green' : 'red') +
' f-10 m-r-15'
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>,
]
return itemRows
}
render() {
let allItemRows = []
this.state.udata.forEach((item, key) => {
const perItemRows = this.renderItem(item, key)
allItemRows = allItemRows.concat(perItemRows)
})
return (
<Aux>
<Row>
<Table hover responsive>
<thead>
<tr>
<th className="sortable" onClick={this.onSort('appName')}>
{' '}
Account Name
</th>
<th> State</th>
<th> Space</th>
<th> Organization</th>
<th className="sortable" onClick={this.onSort('appUpdatedAt')}>
{' '}
Updated At
</th>
</tr>
</thead>
<tbody> {allItemRows}</tbody>
</Table>
</Row>
</Aux>
)
}
Move the sorting part of the function to new a function:
const sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
}
You can use this function in componentDidMount before setting the state with the newData and also in onSort function.
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction)
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
componentDidMount:
componentDidMount() {
// Code
connection.on(
'APIChannel',
function(data) {
let sortedData = []
if (this.state.sort.column) {
sortedData = this.sortData(data, this.state.sort.column,
this.state.sort.direction)
} else {
sortedData = data
}
this.setState({udata: sortedData})
}.bind(this),
)
// Rest of the code
}
EDIT:
import React, { Component } from "react";
import { Row, Col, Form, Card, Table, Tab, Nav } from "react-bootstrap";
import Aux from "../../hoc/_Aux";
import * as signalR from "#aspnet/signalr";
class Dashboard extends Component {
constructor() {
super();
this.state = {
udata: [],
sysdata: [],
expandedRows: [],
user: "active",
system: "",
data: [],
UserFilters: {
appState: [],
orgName: [],
spaceName: []
},
SysFilters: {
appState: []
},
intervalId: 0, //Scroll on top feature
sort: {
column: null,
direction: "desc"
}
};
}
sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
};
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl("/signalserver")
.build();
connection
.start()
.then(function () { })
.catch(function (err) {
return console.error(err.toString());
});
connection.on(
"SBUserBrodcasting",
function (data) {
let sortedData = [];
if (this.state.sort.column) {
sortedData = this.sortData(
data,
this.state.sort.column,
this.state.sort.direction
);
} else {
sortedData = data;
}
this.setState({ udata: sortedData });
}.bind(this)
);
connection.on(
"SBSystemBrodcasting",
function (data) {
this.setState({ sysdata: data });
}.bind(this)
);
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
}
connection.onclose(async () => {
await start();
});
}
onSort(column) {
return function (e) {
let direction = this.state.sort.direction;
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === "asc" ? "desc" : "asc";
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction);
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction
}
});
}.bind(this); // Bind "this" again because the onSort function is returning another function.
}
scrollStep() {
if (window.pageYOffset === 0) {
clearInterval(this.state.intervalId);
}
window.scroll(0, window.pageYOffset - this.props.scrollStepInPx);
}
scrollToTop() {
let intervalId = setInterval(
this.scrollStep.bind(this),
this.props.delayInMs
);
this.setState({ intervalId: intervalId });
}
FilterUserArray = (array, UserFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(UserFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!UserFilters[key].length) return true;
return UserFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
FilterSysArray = (array, SysFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(SysFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!SysFilters[key].length) return true;
return SysFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
HandleRowClick(rowId) {
const currentExpandedRows = this.state.expandedRows;
const isRowCurrentlyExpanded = currentExpandedRows.includes(rowId);
const newExpandedRows = isRowCurrentlyExpanded
? currentExpandedRows.filter(id => id !== rowId)
: currentExpandedRows.concat(rowId);
this.setState({ expandedRows: newExpandedRows });
}
SpaceRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBSpace-" + item}
label={item}
onChange={this.UserAppSpaceFilter.bind(this)}
/>
</li>
];
return itemRows;
}
OrgRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBOrg-" + item}
label={item}
onChange={this.UserAppOrgFilter.bind(this)}
/>
</li>
];
return itemRows;
}
RenderItem(item, key) {
const clickCallback = () => this.HandleRowClick(key);
const itemRows = [
<tr onClick={clickCallback} key={"row-data-" + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
"fa fa-circle text-c-" +
(item.appState === "STARTED" ? "green" : "red") +
" f-10 m-r-15"
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>
];
if (this.state.expandedRows.includes(key)) {
itemRows.push(
<tr key={"row-expanded-" + key}>
<td colSpan="6">
<Card className="card-event">
<Card.Body>
<div className="row align-items-center justify-content-center">
<div className="col">
<h5 className="m-0">Upcoming Event</h5>
</div>
<div className="col-auto">
<label className="label theme-bg2 text-white f-14 f-w-400 float-right">
34%
</label>
</div>
</div>
<h2 className="mt-2 f-w-300">
45<sub className="text-muted f-14">Competitors</sub>
</h2>
<h6 className="text-muted mt-3 mb-0">
You can participate in event{" "}
</h6>
<i className="fa fa-angellist text-c-purple f-50" />
</Card.Body>
</Card>
</td>
</tr>
);
}
return itemRows;
}
onClickfn = () => {
this.setState({ user: "active", system: "inactive" });
};
onClickfnsys = () => {
this.setState({ user: "inactive", system: "active" });
};
UserAppStateFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, appState: options }
});
}
UserAppSpaceFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.spaceName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, spaceName: options }
});
}
UserAppOrgFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.orgName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, orgName: options }
});
}
SysAppStateFilter(e) {
let index;
// current array of options
const options = this.state.SysFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
SysFilters: { ...this.state.SysFilters, appState: options }
});
}
render() {
let Spacefilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.spaceName))).forEach(
(item, key) => {
const perItemRows = this.SpaceRenderFilterList(item, key);
Spacefilterlist = Spacefilterlist.concat(perItemRows);
}
);
let Orgfilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.orgName))).forEach(
(item, key) => {
const perItemRows = this.OrgRenderFilterList(item, key);
Orgfilterlist = Orgfilterlist.concat(perItemRows);
}
);
let allItemRows = [];
this.FilterUserArray(this.state.udata, this.state.UserFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
allItemRows = allItemRows.concat(perItemRows);
}
);
let sysallItemRows = [];
this.FilterSysArray(this.state.sysdata, this.state.SysFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
sysallItemRows = sysallItemRows.concat(perItemRows);
}
);
return (
<Aux>
<Row>
<Col sm={12}>
<Tab.Container defaultActiveKey="user">
<Row>
<Col sm={2}>
<Nav variant="pills" className="flex-column">
<Nav.Item>
<Nav.Link eventKey="user" onClick={this.onClickfn}>
User
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="system" onClick={this.onClickfnsys}>
System
</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Card
style={{
display: this.state.user === "active" ? "" : "none"
}}
>
<Tab.Pane eventKey="user">
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group onReset={this.handleFormReset}>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox1"
value="STARTED"
label="STARTED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox2"
value="STOPPED"
label="STOPPED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
<h6>By Space</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Spacefilterlist}</Form.Group>
</ul>
<h6>By Organization</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Orgfilterlist}</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
<Card>
<Tab.Pane
eventKey="system"
style={{
display: this.state.system === "active" ? "" : "none"
}}
>
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>
<li>
<Form.Check
custom
type="checkbox"
id="chec1"
value="STARTED"
label="STARTED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="chec2"
value="STOPPED"
label="STOPPED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
</Col>
<Col sm={10}>
<Tab.Content>
<Tab.Pane eventKey="user">
<Table hover responsive>
<thead>
<tr>
<th
className="sortable"
onClick={this.onSort("appName")}
>
Account Name
</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th
className="sortable"
onClick={this.onSort("appUpdatedAt")}
>
Updated At
</th>
</tr>
</thead>
<tbody>{allItemRows}</tbody>
</Table>
</Tab.Pane>
<Tab.Pane eventKey="system">
<Table hover responsive>
<thead>
<tr>
<th>App Name</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th>Updated At</th>
</tr>
</thead>
<tbody>{sysallItemRows}</tbody>
</Table>
</Tab.Pane>
</Tab.Content>
</Col>
</Row>
</Tab.Container>
</Col>
<button
id="myBtn"
title="Back to top"
className="scroll"
onClick={() => {
this.scrollToTop();
}}
>
<span className="feather icon-chevron-up" />
</button>
</Row>
</Aux>
);
}
}
export default Dashboard;
Use a parent component to perform the request and pass the unsorted values and sorting order value to a child component.
child component(table component most likely) will display data based on sorting order value.
Currently your component get mounted each time you change the state values
Add a life cycle method called componentWillReceiveProps(nextProps) or you can as well use static getDerivedStateFromProps(props, state) to perform the sorting inside of this method, that way when new data is available it will automatically be sorted alongside with the original one that was there. hence all your other codes remain the same and the new data just takes it rightful place in the sorting.
I have a shopping Cart in react (Not using Redux) which has Quantity input field infront of each item. Qty is 1 by default for all. TotalPrice for each item is infornt of it(unitPrice*1). Now if user updates Qty value, TotalPrice needs to be updated(unitPrice*Qty). My code is doing it but issue is when i update one item Qty, it updates Price, great, but when I update another item Qty, previously updated Price gets reset to default (back to UnitPrice).
I searched it alot but no use. Most People use redux but I dont want to use it. So please help me if u see a solution to it as I want to maintain all the updated prices.
export default class Cart extends Component
{
constructor(props) {
super(props);
this.state = {
data:[],
customer: '',
redirectToReferrer: false,
cart: [],
cartItems: [],
qty: '',
clicked: '' };
this.onChangeQty = this.onChangeQty.bind(this);
}
componentDidMount() {
const retrieved = localStorage.getItem("cartItem");
const arr = JSON.parse(retrieved);
axios.post('http://localhost/Auth/api/customers/show_cart.php', arr,
{
headers: {'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'}
} )
.then(response => {
this.setState({
cartItems :response.data.records
});
})
.catch(error => {
if (error) {
console.log("Error"); }
});
}
onChangeQty(sid,e)
{
this.setState({
clicked: sid,
qty: e.target.value })
}
render()
{
return (
<div id="profileDiv">
<Col md="12" lg="12" sm="12" xs="12">
<h1> <b> Your Shopping Cart </b> </h1>
<Table>
<thead>
<tr className="text-center">
<th> Item# </th>
<th> Image </th>
<th> ID</th>
<th> Name</th>
<th> Unit Price </th>
<th> Quantity </th>
<th> Total Price </th>
</tr>
</thead>
<tbody>
{this.state.cartItems.map( (item, i) =>
<tr>
<td className="text-center">
<h4 key={i}> {i}</h4>
</td>
<td className="text-center">
<Image src={"data:image/png[jpg];base64," + item.Image}
id="cartImage" alt="abc" />
</td>
<td className="text-center">
<h4>{item.SparePartID}</h4>
</td>
<td className="text-center">
<h4> {item.Name}</h4>
</td>
<td className="text-center">
<h4 >{item.Price} </h4>
</td>
<td className="text-center">
<h4 key={item.SparePartID}>
<input className="text-center"
type="number"
min="1"
onChange={(e) => this.onChangeQty(item.SparePartID, e)}
/>
</h4>
</td>
<td className="text-center">
<h4 key={item.SparePartID}>
{
this.state.clicked == item.SparePartID ?
(item.Price*this.state.qty) : item.Price
}
</h4>
</td>
</tr>
)} </tbody>
</Table>
</Col>
</div> ); }
}
What you need to do is, when you get your cart items, to go through each item and to add a quantity for each of them. And add a total in state for the total of all products.
constructor(props) {
super(props);
this.state = {
data:[],
customer: '',
redirectToReferrer: false,
cart: [],
cartItems: [],
totalPrice: 0
clicked: '' };
this.onChangeQty = this.onChangeQty.bind(this);
}
componentDidMount() {
const retrieved = localStorage.getItem("cartItem");
const arr = JSON.parse(retrieved);
axios.post('http://localhost/Auth/api/customers/show_cart.php', arr,
{
headers: {'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'}
} )
.then(response => {
let myItems = [];
response.forEach(item => {
myItems.push({id: item.id, name: item.name, qty: 1, price: item.price, total: item.price});
})
this.setState({
cartItems :myItems
});
})
.catch(error => {
if (error) {
console.log("Error"); }
});
}
OnChange method should find the selected product and update its quantity, and the totalPrice:
onChangeQty(sid,e)
{
const items = this.state.cartItems;
const item = items.find(item => item.id === sid);
const index = items.indexOf(item);
item.qty = e.target.value;
item.total = item.qty * item.price;
items[index] = index;
let totalPrice = 0;
items.forEach(item => {
totalPrice += item.qty * item.price;
});
this.setState({
cartItems: items,
totalPrice
});
}
and in html you can show the total price as
{this.state.totalPrice}
and if you want total of each item you can simply check it by
this.state.cartItems.forEach(item => {
console.log(item.total);
})
I think your problem is the way you are managing your cartItems. In onChangeQty you should iterate over your this.state.cartItems and find the item with the selected sid , you could use
onChangeQty (sid, {target:{value}}){
let {cartItems } = this.state
let ind = cartItems.findIndex( o => o.sid === sid) //get index and
//update the
// qty of
// that element of the cartItems
// array ,
cartItems[ind].sid + = value
this.setState({cartItems})
}
You are using the same state for different for every item, so every changes you do to quantity will modify your state and so the item price of everything. there is no need to use redux for this. You can do two things: move the item logic to a sub component, so , every item will have his own state, or, modify you state to handle an array of objects like {name:'itemname', price:itemPrice etc etc} this way you'll edit only a specific item or improve your cartItems: [] with the qty prop.
EDIT:
now you can try to render n sub components that i've called ChartItem, they will receive {...item} the you can . use from inside your component via this.props.yourItemProp
{this.state.cartItems.map( (item, i) => <ChartItem key={i} {...item} />}
Now, in every ChartItem you can define a private state and use it. If you want to comunicate to the parent component just pass to the child compone t a function like
{this.state.cartItems.map( (item, i) => <ChartItem key={i} {...item}
onItemSomething={this.handleOnItemSomething}
/>}
so, you can call this.props.onItemSomething to comunicate with your parent component
I have a problem. I'm lost in the deep forest of reactJs. I'm new here.
I have 8 components which I need to get via clicking on short-name button .
What are the steps.
I select what I need from the first filter and I get short_names and all the components without data.
I don't want to get all the components, i need just 1 component that I'll get by clicking on a short name.
Screenshot here
Here is code of rendering page:
import React, { Component } from 'react';
import { Link } from 'react-router';
import { getAll, getAllRates } from '../actions';
import { MODULE_NAME } from './index';
import { PanelHeader, PanelFooter, LocalFilter } from 'Components';
import locales from 'Shared/locales';
import search from 'Shared/search';
import sortBy from 'Shared/sortBy';
import { AvsProviders, FakProviders, LaaProviders, Laac1Providers,
Laac2Providers, Laac3Providers, MpgProviders, Mpg2Providers } from '../ComponentsProviders';
export default class ListsAccounting extends Component {
state = {
data: [],
le_id: null,
year: new Date().getFullYear(),
totalPages: 0,
searchString: '',
limit: '50',
page: 1,
};
search = search(this);
sortBy = sortBy(this);
loadData = (params) => {
const { searchString } = this.state;
const q = searchString === '' ? null : searchString;
getAll({ leId: this.state.le_id, name: MODULE_NAME, params: { q, year: this.state.year, ...params } })
.then(success => this.setState({
data: success.data,
totalPages: success.totalPages,
page: success.page,
limit: String(success.limit || ''),
}));
};
constructor() {
super();
this.onClick = this.handleClick.bind(this);
}
handleClick(event) {
const { id } = event.target;
console.log(id);
}
getData = (leId, id, type, year) => {
getAllRates({ leId, id, type, year, name: MODULE_NAME })
.then(() => this.loadData());
};
changeState = state => this.setState(state, () => this.loadData());
render() {
const { limit, totalPages, data } = this.state;
console.log(this);
return (
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-6">
<div className="panel panel-adminme table-dynamic">
<PanelHeader
name="insurances"
currentState={{
state: this.state,
loadData: this.loadData,
}}
/>
<div className="table-filters">
<div className="row no-x-margin">
<div className="col-sm-4 col-xs-6">
<LocalFilter
name="all-providers-leg-entities"
placeholder="le_id"
option="le_id"
value={this.state.le_id}
changeState={this.changeState}
/>
</div>
<div className="col-md-3 col-sm-4 col-xs-6">
<LocalFilter
name="insurance-years"
placeholder="year"
option="year"
value={this.state.year}
changeState={this.changeState}
/>
</div>
</div>
</div>
<div className="table-responsive">
<table className="table table-bordered table-striped table-hover">
<thead>
<tr className="text-center">
<th>
NAME
</th>
<th>
SHORT_NAME
</th>
<th>
ACTION
</th>
</tr>
</thead>
<tbody>
{
!data.length &&
<tr>
<td colSpan="3" className="text-center">
{locales('no_rows')}
</td>
</tr>
}
{
data.map((row, index) => (
<tr key={`${MODULE_NAME}-${index}`}>
<td>
<h5>{row.type}</h5>
</td>
<td>
{
row.text.map((name, indexText) => (
<span key={name} className="margin-right-10">
<Link
key={row.type}
role="button"
onClick={ () => this.getData(
this.state.le_id,
row.text_id[indexText],
row.type,
row.year,
)}
>
{name}
</Link>
</span >
))
}
</td>
<td className="btn btn-info">
ADD
</td>
</tr>
))
}
</tbody>
</table>
</div>
<PanelFooter
limit={limit}
totalPages={totalPages}
loadData={this.loadData}
/>
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-6">
{ data.type === data.type && data.text_id === data.text_id &&
data.map((row) => {
console.log(row.type);
switch (row.type) {
case 'AVS':
return (
<AvsProviders/>
);
case 'FAK' :
return (
<FakProviders/>
);
case 'LAA':
return (
<LaaProviders/>
);
case 'LAAC1':
return (
<Laac1Providers/>
);
case 'LAAC2':
return (
<Laac2Providers/>
);
case 'LAAC3':
return (
<Laac3Providers/>
);
case 'MPG':
return (
<MpgProviders/>
);
case 'MPG2':
return (
<Mpg2Providers/>
);
default:
return null;
}
})
}
</div>
</div>
</div>
);
}
}
Here is page of 1 of the rendering components:
import React, { Component } from 'react';
import { getAllRates } from '../../actions';
import { PanelHeader } from 'Components';
const MODULE_NAME = 'FAK';
export default class FakProviders extends Component {
state = {
data: [],
le_id: null,
year: new Date().getFullYear(),
totalPages: 0,
searchString: '',
limit: '50',
page: 1,
};
componentDidMount() {
this.loadData();
}
loadData = (params) => {
const { searchString } = this.state;
const q = searchString === '' ? null : searchString;
getAllRates({ leId: this.props.params.le_id,
id: this.props.params.text_id,
name: MODULE_NAME,
params: { q, ...params } })
.then(success => this.setState({
data: success.data,
totalPages: success.totalPages,
page: success.page,
limit: String(success.limit || ''),
}));
};
changeState = state => this.setState(state, () => this.loadData());
render() {
const { data } = this.state;
return (
<div className="panel panel-adminme table-dynamic">
<PanelHeader
name="insurances"
currentState={{
search: this.search,
state: this.state,
loadData: this.loadData,
}}
/>
<div className="table-responsive">
<table className="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>
<h4>{data.fak_short_name}</h4>
</th>
<th>
<h4>{data.year}</h4>
</th>
</tr>
</thead>
<tbody>
<tr>
<th>
<h4>fak_rate_ee</h4>
</th>
<th>
<h4>
{data.fak_rate_ee}
</h4>
</th>
</tr>
<tr>
<th>
<h4>fak_rate_er</h4>
</th>
<th>
<h4>
{data.fak_rate_er}
</h4>
</th>
</tr>
</tbody>
</table>
</div>
<div className="panel-footer" />
</div>
);
}
}