Issue
Good Day! I have a simple table with filter functionality in react. The table contains only car name
and price
with dropdown select for filtering cars with their name. I need to display the total price of filtered cars. here is the sandbox .
What I tried was defining a function like this
const updateSubTotal = () => {
var table = document.getElementById("myTable");
let subTotal = Array.from(table.rows)
.slice(1)
.reduce((total, row) => {
return total + parseFloat(row.cells[1].innerHTML);
}, 0);
setTot(subTotal);
};
Unfortunately, this function returns invalid value. Here is the whole code
import { useState } from "react";
import "./styles.css";
const data = [
{
name: "Saab",
price: 100
},
{
name: "Mercedes",
price: 50
},
{
name: "Audi",
price: 10
}
];
export default function App() {
const [tot, setTot] = useState();
const updateSubTotal = () => {
var table = document.getElementById("myTable");
let subTotal = Array.from(table.rows)
.slice(1)
.reduce((total, row) => {
return total + parseFloat(row.cells[1].innerHTML);
}, 0);
setTot(subTotal);
};
//CALCULATE sum of prices
const totalPrice = data.map((item) => item.price).reduce((a, b) => a + b, 0);
const [searchValue, setSearchValue] = useState("");
const DisplayData = data
?.filter((row) => row?.name?.match(new RegExp(searchValue, "i")))
.map((cars) => {
return (
<tr key={cars.name}>
<td>{cars?.name}</td>
<td>{cars?.price}</td>
</tr>
);
});
return (
<div className="App">
<label for="cars">Choose a car:</label>
<select
name="cars"
id="cars"
value={searchValue}
onChange={(e) => {
setSearchValue(e.target.value);
updateSubTotal();
}}
>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
<table className="tables" id="myTable">
<thead className="sticky-header overflow-hidden z-50">
<tr>
<th className="">Car Name</th>
<th className="">Price</th>
</tr>
</thead>
<tbody>{DisplayData}</tbody>
</table>
<div>Total Price without filter: {totalPrice}</div>
<div>Total Price after filter: {tot}</div>
</div>
);
}
Solution
I would suggest using useState hook for the variables and grouping them at the beginning of your component (filteredCars list, filteredTotal)
const [searchValue, setSearchValue] = useState(dropdownItems[0]);
const [filteredData, setFilteredData] = useState(filterData(searchValue));
const [filteredTotal, setFilteredTotal] = useState(
calculateTotal(filteredData)
);
total value doesnt change, so its okay to leave it as a constant
const total = calculateTotal(data);
then you trigger useEffect hook when those states change, in your example everything depends on the dropdown selection, so filtering and summing should be done when selection changes
useEffect(() => {
setFilteredData(filterData(searchValue));
setFilteredTotal(calculateTotal(filteredData));
}, [searchValue, filteredData]);
Bonus tip : Make dropdown items dynamic, create them by spreading names from data array
const dropdownItems = [notSelectedLabel, ...data.map((row) => row.name)];
You can take a look at the codepen here
Answered By - msabic22
Answer Checked By - - Gilberto Lyons (ReactFix Admin)