Reputation: 198
I want to filter my products with onChange
event. For example, If I just search for 'a' I want to see every product that includes 'a' If I delete 'a' or whatever I just searched, I want to see all my products back without refresing the page.
Here is my code below; all it does filter what i search and If I ever delete what I type It doesn't' give me back all the products.
I know I'm mutating my products
with the solution I made but I want to know how to achieve this problem.
import React, { useEffect, useState } from "react";
import { getProductKinds, getProducts } from "../lookup";
function Products() {
//const [count, setCount] = useState(0);
const [products, setProducts] = useState([]);
useEffect(() => {
const myCallBack = (response, status) => {
if (status === 200) {
console.log("products resnpose:", response);
setProducts(response);
}
};
getProducts(myCallBack);
},[] );
const filterProducts = (event) => {
let filterText = event.target.value;
let tempProducts = [...products];
setProducts(tempProducts.filter(product => {
console.log(product.name.includes(filterText));
if (product.name.includes(filterText)) return product;
}))
}
return (
<div className="products-page">
<div className="card-filters">
<form>
<div class="form-search col mt-5 ml-2 ">
<input
onChange = {filterProducts}
type="text"
name="text"
class="form-control"
placeholder="Search"
/>
</div>
<p></p>
<h5 clas>Category</h5>
<hr></hr>
<div className="form-buttons container mt-4">
<button className="btn btn-outline-secondary btn-sm">All</button>
<p></p>
<button className="btn btn-outline-secondary btn-sm">
Electronic
</button>
<p></p>
<button className="btn btn-outline-secondary btn-sm">
Furniture
</button>
<p></p>
<button className="btn btn-outline-secondary btn-sm">Dining</button>
<hr></hr>
</div>
<div>
<div className ="form-price">
<h5>Price</h5>
<p > 2790.99$</p>
<input type="range" name="price" min="0" max="309999" value="309999">
</input>
</div>
</div>
<hr></hr>
</form>
</div>
<div className="card-group container">
{products.map((product) => {
return (
<div>
<div className="card shadow" style={{ width: "18rem" }}>
<img
className="card-img-top"
src="https://dl.airtable.com/.attachmentThumbnails/65708b701baa3a84883ad48301624b44/2de058af"
alt="Card image cap"
/>
<a class="link" href="/products/recroK1VD8qVdMP5H">
<svg
stroke="currentColor"
fill="white"
stroke-width="0"
viewBox="0 0 512 512"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path>
</svg>
</a>
<div className="card-footer">
<p className="card-text">{product.name}</p>
<p className="card-price" style={{ float: "right" }}>
{product.price}$
</p>
</div>
</div>
</div>
);
})}
</div>
</div>
);
}
export default Products;
Upvotes: 0
Views: 1323
Reputation: 2254
Inside filterProducts
you're overwriting the state with the filtered state, so when you remove the search query it's trying to search on items that no longer exist in the state.
To overcome this, you can use React.useMemo
to perform the filtering whilst leaving alone the state you retrieved from the server...
const [products, setProducts] = useState([]);
const [filterText, setFilter] = useState("");
const filterProducts = (event) => {
setFilter(event.target.value);
}
const filtered = React.useMemo(() => {
return products.filter(product => {
return filterText.length > 0 ? product.name.includes(filterText) : true;
})
}, [filterText, products]);
// Use the filtered variable
return (
{filtered.map((product) => {
);
Upvotes: 2
Reputation: 1592
It's not resetting to the default because you're constantly changing the source list. Try doing something like this:
const [filteredProducts, setFilteredProducts] = useState(null)
useEffect(() => {
setFilteredProducts(products)
}, [products])
const filterProducts = (event) => {
let filterText = event.target.value;
let tempProducts = [...products];
setFilteredProducts(tempProducts.filter(product => {
console.log(product.name.includes(filterText));
if (product.name.includes(filterText)) return product;
}))
}
And then use filteredProducts
for your input
.
Upvotes: 0
Reputation: 2292
I would put my response data in 2 arrays, setAllProducts(response);
and setProducts(response);
,
If your input is empty, you show all products by doing setProducts(allProducts)
, if it has 1 character and more you show the products by doing setProducts(YOUR_FILTER_HERE)
I would do something like that using 2 arrays, maybe :
const myCallBack = (response, status) => {
if (status === 200) {
console.log("products resnpose:", response);
setAllProducts(response);
setProducts(response);
}
};
const filterProducts = (event) => {
let filterText = event.target.value;
if (event.target.value.length === 0) {
setProducts(allProducts);
} else {
setProducts(products.filter(product => {
console.log(product.name.includes(filterText));
if (product.name.includes(filterText)) return product;
}))
}
}
Upvotes: 1