Conditionally adding an extra LI to a list in React - reactjs

I have a simple React component that iterates over a list of items, and each item includes it as a Component. The trick is every 3rd list item, I want to inject a "special" extra li.
So, my "pseudo react code" looks something like this:
return (<ul>
{
data.myItems.items.map((item, index) => {
return (
<li><MyItem key={index} {...item} /></li>
{ (index % 3 === 0) &&
<li>Special LI</li>
}
);
})
}
</ul>);
This obviously doesn't work since my return(..) potentially returns 2 top-level elements (2 li's).
I can't wrap the 2 divs in something else it will break the semantics of ul/li (i need them all to be siblings in a list of depth 1).
I figure there has to be a simple way to do this that I'm overlooking.

Just wrap it in an empty tag. In react, it is called Fragment.
return (<ul>
{
data.myItems.items.map((item, index) => {
return (
<>
<li><MyItem key={index} {...item} /></li>
{ (index % 3 === 0) &&
<li>Special LI</li>
}
</>
);
})
}
</ul>);

You can use a fragment element. More on fragments here.
return (<ul>
{
data.myItems.items.map((item, index) => {
return (
<>
<li><MyItem key={index} {...item} /></li>
{ (index % 3 === 0) &&
<li>Special LI</li>
}
</>
);
})
}
</ul>);

Related

Component is not being returned in nested loop

I am trying to render the child component inside a nested loop. However it is not being render in the second loop(red tick). Although it is rendered normally in the first loop (blue tick). Kindly highlight why is it no rendered in the second loop.
https://i.stack.imgur.com/LFiKU.png
Codesandbox Link : https://codesandbox.io/s/competent-nova-u9rzuh?file=/src/parent.js
import React from "react";
import ProductFeaturesCards from "./ProductFeaturesCards.js";
import { Products } from "../ProductsData.js";
const ProductFeatures = ({ className = "", id }) => {
return (
<section id='product-features' className={`${className}`}>
<div className='container'>
<div className='row d-flex justify-content-center'>
<div className='col-lg-12 col-md-12 col-sm-12 col-12 py70'>
<p className='features-title'>Product Features</p>
</div>
</div>
<div className='row'>
{Products.forEach((item, i) => {
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
})}
</div>
</div>
</section>
);
};
export default ProductFeatures;
Can you try this. Not sure if this will work
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
return Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
The first mistake you did was using forEach. forEach will mutate the data and will not return anything. So, Instead you need to use map which doesn't mutate and returns the result.
The 2nd mistake is the return statement not added inside the if condition for the map. So, it never gets returned and hence your first map will not receive the value.
After this you should be able to run it.
<div className="row">
{Products.map((item) => { // replaced forEach with map
if (item.id === id) {
return Object.values(item.Product_features[0]).map( // return added
(feature, index) => {
return <Card data={feature} key={index} />;
}
);
}
})}
</div>

JSX map inside a map is it possible?

I'm trying to map over a map in JSX but the second inner map doesn't render why?
{food.map((item: ItemsShape, index: number) => {
return (
<div key={`${index}`}>
<div>{item.Quantity}</div>
<div>{item.Name}</div>
{item.Options && <div>{JSON.stringify(item.Options)}</div>} // <= this shows al the options
{item.Options &&
item.Options.map((option: any) => {
<div>OPTION {option.Name}</div>;
})} // <= This doesnt even render why?
</div>
);
})}
You missed return
{food.map((item: ItemsShape, index: number) => {
return (
<div key={`${index}`}>
<div>{item.Quantity}</div>
<div>{item.Name}</div>
{item.Options && <div>{JSON.stringify(item.Options)}</div>} // <= this shows al the options
{item.Options &&
item.Options.map((option: any) => {
return (<div>OPTION {option.Name}</div>);
})}
</div>
);
})}

If statement in react .map function

I'm trying to only map a function to listItems if the condition is true, otherwise skip it. I have been trying different stuff but I don't want to return before the list is complete.
I have this code:
const listItems = (
<ul>
{desclist.map((point, index) =>
if (point.length < 2) {
<li key={index}>
<p>{point}</p>
</li>
)}
}
</ul>
);
return listItems;
}
If you return something within the .map function, it won't end the loop of the function and will still continue iterating. Instead, what you return will be put within the resulting array that .map returns. Meaning that if inside the .map you return a component depending on an if statement, and otherwise none, the result will be an array with all the components that were returned.
const listItems = (
<ul>
{
desclist.map((point, index) =>
if (point.length < 2) {
return <li key={index}>
<p>{point}</p>
</li>;
}
);
}
</ul>
);
If else will work under parentheses and also don’t use index directly as key instead append some text along with index to key prop so
Change
{desclist.map((point, index) =>
if (point.length < 2) {
<li key={index}>
<p>{point}</p>
</li>
)}
}
To
{desclist.map((point, index) => {
if (point.length < 2) {
return (<li key={'Key-'+index}>
<p>{point}</p>
</li>
)}
}
)}

Mapping array to JSX elements inside rows

Using React I am trying to map an array of elements into a containing HTML element.
However I can not figure out how to map two elements into one HTML element since these elements will have to be closed.
This is an example of what I would like to do. Map two components inside a containing element of className="row" but I can not because the JSX elements have to be closed:
const CalendarGrid = (events) => {
let content = [];
events.events.map((event, index) =>{
if (index % 2 != 0) {
content.push(
<div className="row">
<EventCardRight key={event.id} event={event} align="right"/>
)
}if (index % 2 == 0)
{
content.push(
<EventCardLeft key={event.id} event={event} />
</div className="row">
);
}
});
return (
<div>
{content}
</div>
);
}
You can take advantage of inline logical operators and map through events directly as:
const CalendarGrid = (events) => {
return (
<div>
{events.events.map((event, index) =>
<div key={event.id} className="row">
{index % 2 != 0 ? (
<EventCardRight key={event.id} event={event} align="right"/>
) : (
<EventCardLeft key={event.id} event={event} />
)}
</div>
)}
</div>
)
}
First, I would split your array to chunks of size 2. See How to split a long array into smaller arrays, with JavaScript, I will use lodash for that:
const rows = _.chunk(events.events, 2);
now I can simply map every row to elements:
const content = rows.map((rowEvents, index) => (
<div key={index} className="row">
<EventCardRight event={rowEvents[0]} align="right" />
<EventCardLeft event={rowEvents[1]} />
</div>
));

How to do condtitional rendering in React and ES6 inside the map function

I'm trying to render an array of messages but would want it to render differently by class given a condition my code looks like this:
render() {
return (
<div>
{this.props.messages.map((m, index) => (
//if m.id === 1 render this:
<p className={someClass1}>Hello, {m.message}!</p>
//else render this:
<p className={someClass2}>Hi, {m.message}!</p>
))}
</div>);
}
you can easily add logic to your map. you just need the contents to not be an inline return of a react component.
render() {
return (
<div>
{this.props.messages.map((m, index) => {
if (m.id === 1){
return <p className={someClass1}>Hello, {m.message}!</p>
}
return <p className={someClass2}>Hi, {m.message}!</p>
})}
</div>
);
}
You can also do the same thing with a forEach outside of the return on your render like so
render() {
const elems = [];
this.props.messages.forEach( (m, index) => {
if (m.id === 1) {
elems.push(<p className={someClass1}>Hello, {m.message}!</p>);
} else {
elems.push(<p className={someClass2}>Hi, {m.message}!</p>);
}
return (
<div>
{elems}
</div>
);
}

Resources