I have a search class component. However, when i search for clients by their names based on ObjectId(clientdetails._id), rendering returns multiple tables equivalent to the number of documents with that ObjectId.Moreover, the total bill calculated gives the total cost from all objectIds instead of the total cost derived from documents with similar ObjectId (clientdetails._id)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const isSearched = searchTerm => item => {
if (searchTerm === "") {
return;
}
return item.clientdetails.fullName.toLowerCase().includes(searchTerm.toLowerCase());
};
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list:[
{
"_id": "6389d19a3b28b9e4af3694f8",
"clientdetails": {
"_id": "6389d1193b28b9e4af3694f2",
"fullName": "WADE IVAN",
"dateofBirth": "1990-01-01T00:00:00.000Z",
"age": 32,
"__v": 0
},
"paymentReferenceCode": "1",
"paymentType": "Cash",
"paymentDescription": "ACCOMODATION",
"procedureAmount": "100",
"paymentDiscount": 20,
"AmountPaid": 100,
"Total": 100,
"__v": 0
},
{
"_id": "6389d1bb3b28b9e4af369500",
"clientdetails": {
"_id": "6389d1193b28b9e4af3694f2",
"fullName": "WADE IVAN",
"dateofBirth": "1990-01-01T00:00:00.000Z",
"age": 32,
"__v": 0
},
"paymentReferenceCode": "12",
"paymentType": "Cash",
"paymentDescription": "ACCOMODATION",
"procedureAmount": "100",
"paymentDiscount": 20,
"AmountPaid": 80,
"Total": 80,
"__v": 0
},
{
"_id": "6389d1e03b28b9e4af369508",
"clientdetails": {
"_id": "6389d0ed3b28b9e4af3694ee",
"fullName": "JOHN MARK",
"dateofBirth": "2000-01-12T00:00:00.000Z",
"age": 22,
"__v": 0
},
"paymentReferenceCode": "3",
"paymentType": "Cash",
"paymentDescription": "100",
"procedureAmount": "100",
"paymentDiscount": 20,
"AmountPaid": 200,
"Total": 200,
"__v": 0
},
{
"_id": "6389d2173b28b9e4af369510",
"clientdetails": {
"_id": "6389d0ed3b28b9e4af3694ee",
"fullName": "JOHN MARK",
"dateofBirth": "2000-01-12T00:00:00.000Z",
"age": 22,
"__v": 0
},
"paymentReferenceCode": "9",
"paymentType": "Cash",
"paymentDescription": "ACCOMODATION",
"procedureAmount": "10",
"paymentDiscount": 2,
"AmountPaid": 300,
"Total": 300,
"__v": 0
}
],
searchTerm: ""
};
}
onSearchChange = event => {
this.setState({ searchTerm: event.target.value });
};
render() {
const { searchTerm, list } = this.state;
return (
<div className="page">
<div className="interactions">
<Search
value={searchTerm}
SearchChange={this.onSearchChange}
>
Search
</Search>
<Table list={list} pattern={searchTerm} onDismiss={this.onDismiss} />
</div>
</div>
);
}
}
const Search = ({ value, SearchChange, children }) => {
return (
<form>
{children}
<input type="text" value={value} onChange={SearchChange} />
</form>
)
}
class Table extends React.Component {
render() {
const { list, pattern } = this.props;
const total=(list.reduce((total,item) => total = total item.AmountPaid , 0 ));
return (
<div className="table">
{list.filter(isSearched(pattern)).map(item => (
<div key={item.clientdetails.fullName} className="table-row" >
<table>
<thead>
<tr>
<th>Client Name </th>
<th>Payment Reference Code</th>
<th>Payment Type</th>
<th>Payment Description</th>
<th>Procedure Amount</th>
<th>Payment Discount </th>
<th>Amount Paid </th>
</tr>
</thead>
<tbody>
{list.filter(isSearched(pattern)).map(item => (
<tr >
<td>{item.clientdetails.fullName}</td>
<td>{item.paymentReferenceCode}</td>
<td>{item.paymentType}</td>
<td>{item.paymentDescription}</td>
<td>{item.procedureAmount}</td>
<td>{item.paymentDiscount}</td>
<td>{item.AmountPaid}</td>
</tr>
))}
<tfoot><tr>
Total:
{total}
</tr>
</tfoot>
</tbody>
</table>
</div>
))}
</div>
);
}
}
How can i improve this code. Thanks in advance
CodePudding user response:
Here is the basic solution
let items = [
{
_id: "6389d19a3b28b9e4af3694f8",
clientdetails: {
_id: "6389d1193b28b9e4af3694f2",
fullName: "WADE IVAN",
dateofBirth: "1990-01-01T00:00:00.000Z",
age: 32,
__v: 0
},
paymentReferenceCode: "1",
paymentType: "Cash",
paymentDescription: "ACCOMODATION",
procedureAmount: "100",
paymentDiscount: 20,
AmountPaid: 100,
Total: 100,
__v: 0
},
{
_id: "6389d1bb3b28b9e4af369500",
clientdetails: {
_id: "6389d1193b28b9e4af3694f2",
fullName: "WADE IVAN",
dateofBirth: "1990-01-01T00:00:00.000Z",
age: 32,
__v: 0
},
paymentReferenceCode: "12",
paymentType: "Cash",
paymentDescription: "ACCOMODATION",
procedureAmount: "100",
paymentDiscount: 20,
AmountPaid: 80,
Total: 80,
__v: 0
},
{
_id: "6389d1e03b28b9e4af369508",
clientdetails: {
_id: "6389d0ed3b28b9e4af3694ee",
fullName: "JOHN MARK",
dateofBirth: "2000-01-12T00:00:00.000Z",
age: 22,
__v: 0
},
paymentReferenceCode: "3",
paymentType: "Cash",
paymentDescription: "100",
procedureAmount: "100",
paymentDiscount: 20,
AmountPaid: 200,
Total: 200,
__v: 0
},
{
_id: "6389d2173b28b9e4af369510",
clientdetails: {
_id: "6389d0ed3b28b9e4af3694ee",
fullName: "JOHN MARK",
dateofBirth: "2000-01-12T00:00:00.000Z",
age: 22,
__v: 0
},
paymentReferenceCode: "9",
paymentType: "Cash",
paymentDescription: "ACCOMODATION",
procedureAmount: "10",
paymentDiscount: 2,
AmountPaid: 300,
Total: 300,
__v: 0
}
];
const reduced = items.reduce((acc, curr) => {
//console.log(curr);
const prop = curr.clientdetails._id;
const existingTotal = acc?.[prop]?.total ?? 0;
return {
...acc,
[prop]: {
id: prop,
name: curr.clientdetails.fullName,
total: existingTotal curr.Total
}
};
}, {});
const list = Object.entries(reduced).map(([key, value]) => {
return {
id: key,
name: value.name,
total: value.total
};
});
console.log("reduced", reduced);
console.log("list", list);
To include it in component
// if only final total is needed, this function can be optimized
// if grouping is needed then total needs to be gropued for each entry
function getTotal(items) {
try {
const reduced = items?.reduce((acc, curr) => {
const prop = curr.clientdetails._id;
const existingTotal = acc?.[prop]?.total ?? 0;
console.log(acc, acc?.[prop]);
console.log(acc, curr.clientdetails.fullName);
return {
...acc,
[prop]: {
id: prop,
name: curr.clientdetails.fullName,
total: existingTotal curr.Total
}
};
}, {});
const total = Object.entries(reduced).reduce((acc, [key, value]) => {
return acc value.total
}, 0);
return total;
} catch (error) {
console.log("error", error);
return 0;
}
}
// for grouping filtered items
function groupItems(items) {
try {
const grouped = items?.reduce((acc, curr) => {
const prop = curr.clientdetails._id;
//const id = curr.clientdetails._id;
const existingTotal = acc?.[prop]?.total ?? 0;
let entries = acc[prop]?.entries ?? [];
entries.push({
id: curr._id,
total: curr.Total,
paymentType: curr.paymentType
// include more props
});
return {
...acc,
[prop]: {
name: curr.clientdetails.fullName,
total: existingTotal curr.Total,
entries
}
};
}, {});
console.log("list", grouped);
const list = Object.entries(grouped).map(([key, value]) => {
return {
id: key,
name: value.name,
total: value.total,
entries: value.entries
};
});
return list;
} catch (error) {
console.log("error", error);
return [];
}
}
/*
will contain list of
{
id: key,
name: value.name,
total: value.total,
// contains individual entries and the total for the grouped entries
entries: [
{
id: curr._id,
total: curr.Total,
paymentType: curr.paymentType
}
]
}
*/
class Table extends React.Component {
render() {
const { list, pattern } = this.props;
const filteredList = list.filter(isSearched(pattern));
const grouped = groupItems(filteredList);
/*
will contain list of
{
id: key,
name: value.name,
total: value.total,
// contains individual entries and the total for the grouped entries
entries: [
{
id: curr._id,
total: curr.Total,
paymentType: curr.paymentType
}
]
}
*/
// TODO
// you can write function to get total from grouped items
const total = getTotal(filteredList)
return (
<div className="table">
//you can show the gropued entries here
{grouped?.map(({ id, total: grpTotal, entries = [] }) => (
<div key={item.id} className="table-row" >
This code an be optimized for performance and made simpler with functional components.
Hope it helps