Reputation: 107
I'm trying to set a conditional rendering of a mapped nested object. Mapped object:
const products = [
{category: "Napoje", products:
{
product1: "Piwo",
product2: "Wino",
product3: "Cola",
product4: "Sprite"
}
},
{category: "Pieczywo", products:
{
product1: "Chleb",
product2: "Bułka",
product3: "Chałka",
product4: "Rogal"
}
}
]
My code:
class Products extends React.Component{
constructor(props){
super(props)
}
render(){
for(var i=0; i<this.props.products.length; i++){
if (Object.values(this.props.products[i].products).indexOf(this.props.filter)<1) return false;
}
return(
<div>
{this.props.products
.map(product =>
<div>
{product.category}
{Object.values(product.products).map(name => <li>{name}</li>)}
</div>
)
}
</div>
)
}
}
How can I fix that? The component recives text from search input. The idea is to show only objects that contains the text form input.
Thanks,
Kuba
Upvotes: 0
Views: 904
Reputation: 2781
Here is a slightly optimized version where objects are mapped to objects with only matched products or null
s and then filtered out for only non-null
s:
class Products extends React.Component {
getFilteredProducts() {
const filter = this.props.filter;
const filteredProducts = this.props.products.map(obj => {
// filter out those that don't match
const filtered = Object.values(obj.products)
.filter(p => p.indexOf(filter) > -1);
// if no match found, exclude from the list entirely
if (filtered.length === 0) return null;
// combine into a new object with only filterd products
return { ...obj, ...{ products: filtered } };
})
// filter out objects without any matched products
.filter(product => product);
return filteredProducts;
}
render() {
return (
<div>
{this.getFilteredProducts()
.map((product, ind) =>
<div key={ind}>
{product.category}
{Object.values(product.products).map(name => <li>{name}</li>)}
</div>
)
}
</div>
)
}
}
Upvotes: 1
Reputation: 3687
class Products extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div>
{this.props.products
.map(product =>
<div>
{product.category}
{Object.values(product.products).map((name) =>{
if(name.indexOf(this.props.filter) === -1) {
return null
}
return (<li>{name}</li>)
} )}
</div>
)
}
</div>
)
}
}
This may help
Upvotes: 0
Reputation: 32076
When you need to select only certain elements from an array, a common and good pattern is to filter the array. Here's unoptimized code:
class Products extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div>
{this.props.products
.filter(product => Object.values(product.products).filter(
productName => productName.indexOf(this.props.filter) === -1 )
)
.map(product =>
<div>
{product.category}
{Object.values(product.products).map(name => <li>{name}</li>)}
</div>
)
}
</div>
)
}
}
I would abstract out the filtering function into a method:
class Products extends React.Component{
constructor(props){
super(props)
}
// Return every product name in a category that doesn't match a
// specified string
filterProducts(products, testString) {
return products.filter(product =>
Object.values(product.products).filter(
productName => productName.indexOf(testString) === -1
)
);
}
render(){
const { products, filter } = this.props;
return(
<div>
{this.filterProducts(prodcts, filter).map(product => (
<div>
{product.category}
{Object.values(product.products).map(name => <li>{name}</li>)}
</div>
)}
</div>
)
}
}
Upvotes: 0