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>
));
Related
My console keep giving me this error
Warning: Each child in a list should have a unique "key" prop.
despite that I gave a unique key to my map list. My code looks like this:
const [cardItem, setCardItem] = useState(data);
const uniqueIds = [];
const uniqueCat = cardItem.filter((element) => {
const isDuplicate = uniqueIds.includes(element.category);
if (!isDuplicate) {
uniqueIds.push(element.category);
return true;
}
return false;
});
return (
<>
<div className="art-container">
{uniqueCat
.filter((id) => id.id < 73)
.map((item, i) => {
return item.category === "Filmmaking" ||
item.category === "Multimedia" ? (
<div key={i}>
<div className="art-projects-contants">
<h1>{item.category}</h1>
<Card cardItem={cardItem} cat={item.category} />
</div>
</div>
) : (
<>
<div key={i} className="art-img-projects-contants" >
<h1>{item.category}</h1>
<Img cardItem={cardItem} cat={item.category} />
</div>
</>
);
})}
</>
);
I tried to give it a key with the id by {item.id} also giving the second div a key but it still give me the same error. Could the problem be from the uniqueCat because it gave two arrays when I console log it as in the picture?
How can I fix the Unique key problem?
Note: I am using map function in other places in my project but its the only place I have this error.
Your first div has a key, while your second does not:
<div className="art-container">
{uniqueCat
.filter((id) => id.id < 73)
.map((item, i) => {
return item.category === "Filmmaking" ||
item.category === "Multimedia" ? (
<div key={i}>
<div className="art-projects-contants">
<h1>{item.category}</h1>
<Card cardItem={cardItem} cat={item.category} />
</div>
</div>
) : (
<>
<div key={i} className="art-img-projects-contants" > // <== add key here
<h1>{item.category}</h1>
<Img cardItem={cardItem} cat={item.category} />
</div>
</>
);
})}
</div>
The key must be unique on every element in the component tree, your index (i) may have already been used on a different element, avoid using just the loop index as the key.
It's best to use an actual id from the filtered uniqueCat because this id will most likely be unique from any other ids.
Sample code
<div className="art-container">
{uniqueCat
.filter((id) => id.id < 73)
.map((item) => {
return item.category === 'Filmmaking' ||
item.category === 'Multimedia' ? (
<div key={item.id}>...</div>
) : (
<div key={item.id}>...</div>
);
})}
</div>;
If the ids isn't very unique then you can do some form concatenation with template strings using the loop index.
Like this
<div className="art-container">
{uniqueCat
.filter((id) => id.id < 73)
.map((item, i) => {
return item.category === 'Filmmaking' ||
item.category === 'Multimedia' ? (
<div key={`{item.id}-${i}`}>...</div>
) : (
<div key={`{item.id}-${i}`}>...</div>
);
})}
</div>;
I'm starting to ReactJs and have a doubt when generating a list with an ordering. How do I include a counter inside my map?
The way I'm doing it is repeating the number 1 in all records. I wanted each record to have a counter (1 Adam, 2 Andrew, 3 Sophia ...)
<div>
{
data.contacts.data.map((contact) => {
return (
<div>
<span>1</span> <b>{contact.data.name} {contact.data.email}</b>
</div>
)
})
}
</div>
Use the second argument (array index) of the map method:
<div>
{
data.contacts.data.map((contact, index) => (
<div>
<span>{index + 1}</span> <b>{contact.data.name} {contact.data.email}</b>
</div>
))
}
</div>
With map you have access to the index. So you can use the index to add a counter. Also, remember to add a key-value to each child you're printing with map (you can use the index for this).
<div>
{
data.contacts.data.map((contact, index) => {
return (
<div key={index}>
<span>{index + 1}</span> <b>{contact.data.name} {contact.data.email}</b>
</div>
)
})
}
</div>
How to iterate and display each element from 'grandChildren' list -> for eg: ["AQ11", "AS12"]. Thank you.
responseData:
[{Key:"A",Value:["AQ11", "AS12"]}, {Key:"C",Value:["CN22", "CL33", "CV44"]}].
Have set KeyValuePair's key to 'children' and Value to 'grandChildren' state.
I am able to display children but not grandChildren as it returns list of values ["AQ11", "AS12"]. Tried below but could not succeed
render() {
const child= Object.values(this.state.items);
const grandChild= Object.values(this.state.items[i].grandChildren); // Not sure how to set - how do I pass value of i.
return (
<div>
{child.map((element, index) => {
<div key={index + element.children}>
return (
<Autocomplete
value={element.children} // this displays children like "A" OR "C"
/>
{grandChild.map((element1, index) => (
<Autocomplete
value={element.grandChildren[element1]} // NOT SURE HOW TO GET THIS TO WORK
/>
))}
</div>
))}
);
</div>
How do I iterate and display value of grandchildren
You can do it like this
render(){
const child = Object.values(this.state.items);
return (
<div>
{child.map((element, index) => {
const grandChild = Object.values(this.state.items[index].grandChildren);
return (
<div key={index}>
<Autocomplete
value={element.children} // this displays children like "A" OR "C"
/>
{grandChild.map((element1, index) => (
<Autocomplete
value={element1}
/>
))}
</div>
);
})}
</div>
);
};
I'm obviously not doing this the React way.
I have a collection of comparison objects that are shown
if there is more than one, then I want to include a wrapper element around this collection
In Vue.js I would do this with a v-show or v-if for instance. How do I do this in React?
{/* COMPARISONS */}
{this.getCurrentItem().comparisons!.length > 0 &&
(
<div className="comparisonAreas">
)
}
{
this.getCurrentItem().comparisons!.map((comparison: IComparison) => {
return (
<div className="comparison">
<h3 dangerouslySetInnerHTML={{ __html: this.getSmartVersusLine(comparison, this.getCurrentItem()) }} />
<div dangerouslySetInnerHTML={{ __html: this.convertComparisonBodyToBodyParsed(comparison) }} />
</div>
)
})
}
{this.getCurrentItem().comparisons!.length > 0 &&
(
</div>
)
}
Extract the middle into a variable before and then render the two cases:
const MyComp = (props) => {
const comparisonObjects = ...
if(comparisionObjects.length <= 1) {
return comparisonObjects
} else {
return <div>{comparisonObjects}</div>;
}
}
Edit: You should add a key prop in your array components. See https://reactjs.org/link/warning-keys
You could use a ternary operator :
{this.getCurrentItem().comparisons!.length>0 ?
<div className="comparisonAreas">getCurrentItem.comparisons!.map...</div>
: getCurrentItem.comparisons!.map...
}
React group 4 items in a div with class row. At the moment you can see below I have a <div> group around the articles.
// articles is array of article object
<div className="row">
<section className="section--6-or-4-items section-featured campaign-teasers">
{articles.map(item => <Teaser {...item} key={item.id} />)}
</section>
</div>
Should I create a new component which have <div> wrapper around and only accept 4 arrays at a time to render out the articles.
Your best bet here is to build your array of articles into an array of 4-length arrays. You can do this using lodash's handy chunk method.
Once you've chunked your articles, you can iterate over them in your JSX, something like this:
const sections = chunk(articles, 4);
return (
<div className="row">
{sections.map((articles, i) => (
<section className="..." key={i}>
{articles.map(item => <Teaser {...item} key={item.id} />)}
</section>
)}
</div>
);
Edit: If you don't want to use lodash, you can use reduce to great effect:
function chunk(array, size) {
return array.reduce((chunks, item, i) => {
if (i % size === 0) {
chunks.push([item]);
} else {
chunks[chunks.length - 1].push(item);
}
return chunks;
}, []);
}
Personally, I extract rendering to dedicated methods.
<div className="row">
<section className="section--6-or-4-items section-featured campaign-teasers">
{ this.renderArticles() }
</section>
</div>
Renderer method here...
renderArticles() {
return createChunks(articles, 4).map((chunk, idx) => (
<div key={idx}>
{ chunk.map((props, idx) => <Teaser key={idx} {...props} />) }
</div>
));
},
And finally, function which chunks array into smaller array on N size
const createChunks = (array, size) => {
const copy = array.concat();
const chunks = [];
while (copy.length) {
chunks.push( copy.splice(0, size) );
}
return chunks;
}