How can I filter the filtered array? So let's say that I filter the products by price. Assume the value of the input range slider (price) is less than a thousand dollars then it will return an array that is less than the said value which is a thousand dollars. Now, if I want to filter again using the brand or category filter buttons, so for example I chose the brand 'ikea' from the brand filters. How can I filter the returned array from the filtered price? Right now, when I filter the products by the price it can return an array but when I filter again by category/brand then it will return an array that is not coming from the filtered price array.
Here are my codes:
export const state = {
products: [], //this is where I stored all the products coming from the API
filters: {
categories: [], //this is where I stored all the categories
brands: [], //this is where I stored all the brand
price: 0, //this is where I stored the max price from the array
},
};
@param value - an integer value that comes from the input range slider
export const loadFilterPrice = function (value) {
return state.products.filter(product => product.price <= value);
};
@param type - a string value 'brand' or 'category' @param value - a string value of brands/categories,
export const loadFilterProductByType = function (type, value) {
if (value === 'all') return state.products;
return state.products.filter(product => product[type] === value);
};
CodePudding user response:
You filtered products but you haven't assigned a new variable/state for filtered products
You can add a filteredProducts
variable here
export const state = {
products: [], //this is where I stored all the products coming from the API
filteredProducts: [] //this should be a clone of `products`
filters: {
categories: [], //this is where I stored all the categories
brands: [], //this is where I stored all the brand
price: 0, //this is where I stored the max price from the array
},
};
Whenever you assign a value for products
from an API call initially, you can assign filteredProducts
value too
//`respondedProducts` is from API calls
state.products = [...respondedProducts]
state.filteredProducts = [...respondedProducts]
Here is how we use it
export const loadFilterPrice = function (value) {
if(!value) return state.products; //return the original product list
return state.filteredProducts.filter(product => product.price <= value);
};
state.filteredProducts = loadFilterPrice(1000) //need to update filtered products variable
Similarly, we can apply it to loadFilterProductByType
export const loadFilterProductByType = function (type, value) {
if (value === 'all') return state.products; //return the original product list
return state.filteredProducts.filter(product => product[type] === value);
};
state.filteredProducts = loadFilterProductByType('brand', 'ikea') //need to update filtered products variable
CodePudding user response:
This solution specifically attempts to answer below point:
What should I do?
There are several ways to go about this. The optimal ways may be to use useMemo, useCallback, etc.
The below is just one way that it may be done, which relies only on useState
and useEffect
.
const {useState, useEffect} = React;
const Thingy = ({initState, ...props}) => {
const [products, setProducts] = useState(
[
...initState.products.map(
x => ({...x, filtered: false})
)
]
);
const [filters, setFilters] = useState({
categories: "", brands: "", price: 0 // Number.MAX_VALUE
});
const [showFilters, setShowFilters] = useState(false);
const [filterPrice, setFilterPrice] = useState(0);
const renderFilterFor = col => (
<div>
<label> {col} </label>
<select
name={col}
onChange={e => {
const v = e.target.value;
setFilters(prev => ({
...prev,
[col]: v !== 'None' ? v : ""
}));
}}
>
{
[
'None', ...new Set(products.map(ob => ob[col]))
].map(cat => (
<option value={cat}>{cat}</option>
))
}
</select>
</div>
);
const applyFilter = pr => (
Object.entries(filters)
.filter(([k, v]) => ((k === 'price' && v > 0) || v.length > 0))
.every(([k, v]) => ((k === 'price' && pr[k] <= v) || pr[k] === v))
);
useEffect(
() => setProducts(
prev => prev.map(
p => ({
...p,
filtered: applyFilter(p)
})
)
),
[filters]
);
return (
<div>
<button
onClick={() => setShowFilters(prev => !prev)}
>
{showFilters ? 'Hide' : 'Show'} Filters
</button>
{
showFilters && (
<div >
<div>{renderFilterFor("categories")}</div>
<div>{renderFilterFor("brands")}</div>
<label for="priceInp">Enter price: </label>
<input
type="number" value={filterPrice} min={0}
onChange={e => {
const v = e.target.value;
setFilterPrice(v);
setFilters(prev => ({
...prev,
price: v
}));
}}
/>
</div>
)
}
{
products.filter(({filtered}) => filtered).map(
({productId, productName, categories, brands, price}) => (
<div key={productId}>
{productName}
<b>Category: </b>{categories}
<b>Brand: </b>{brands}
<b>Price: </b>{price}
</div>
)
)
}
</div>
);
};
const initState = {
products: [...Array(15).keys()].map(i => ({
productId: i 1,
productName: `Product - ${i 1}`,
categories: `Category - ${i % 3 1}`,
brands: `Brand - ${i % 4 1}`,
price: Math.floor(Math.random() * ((i 1) * 5))
}))
};
ReactDOM.render(
<div>
DEMO
<Thingy initState={initState}/>
</div>,
document.getElementById("rd")
);
.singleProduct {
border: 2px solid grey;
margin: 5px;
width: 85%;
}
.singleProduct > *{
margin: 15px;
padding: 5px;
}
.filtersLine {
display: flex;
margin: 15px;
align-items: center;
justify-content: space-between;
width: 85%;
}
.filtersLine > label { margin: 10px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>