I have 2 arrays of objects: meeting and room and a Card component with some fields to be completed by them.
I tried to map over the arrays in order to access the elements from both of them at the same time but it returns the Card component six times(the number of the elements in the array of Room)
<div className="topCards">
{room.map((dataObj) => {
return (
<div>
{meeting.map((meetObject) => {
return (
<div className="item">
<Card3
className="card3"
teamName={meetObject.name}
roomName={dataObj.name}
time={dataObj.time}
data={dataObj.data}
capacity={dataObj.capacity}
/>
</div>
);
})}
</div>
);
})}
</div>
So, my question is how can I return the Card element with elements from both cards
What let you think that it would work like that?
Just use the index parameter of Array.prototype.map
<div className="topCards">
<div>
{meeting.map((meetObject, i) => {
return (
<div className="item">
<Card3
className="card3"
teamName={meetObject.name}
roomName={dataObj[i].name}
time={dataObj[i].time}
data={dataObj[i].data}
capacity={dataObj[i].capacity}
/>
</div>
);
})}
</div>
</div>
Related
I have a News feature where user can post their status. However, so far, I cannot make it display all comments of the news as my current solution only allows two-level only. Here are my codes:
News.js (for all)
function News() {
const {userId, setUserId} = useContext(UserContext);
const {type, isUserType} = useContext(UserTypeContext);
const [newsList, setNewsList] = useState([]);
const rootNews = newsList.filter(
(newList) => newList.reply_of === null
);
const getReplies = commentId => {
return newsList.filter(newList => newList.reply_of === commentId);
}
useEffect(()=>{
Axios.get('http://localhost:3001/news',{
})
.then((response) => {
if(response.data.length > 0){
setNewsList(response.data);
}
})
},[]);
return (
<div className = "news p-5">
<h3 className="news-title">News</h3>
<div className = "comments-container">
{rootNews.map((rootNew) => (
<div>
<Comment key={rootNew.news_id}
comment={rootNew}
replies={getReplies(rootNew.news_id)}/>
</div>
))}
</div>
</div>
)
}
Comment.js (for rendering the comments and replies)
function Comment({comment, replies}) {
return (
<div className="comment">
<div className="comment-image-container">
<img src = "/user-icon.png" />
</div>
<div className="comment-right-part">
<div className="comment-content">
<div className="comment-author">
{comment.user_name}
</div>
<div>{comment.day}, {moment(comment.date).format('DD-MM-YYYY')} at {comment.time}</div>
</div>
<div className="comment-text">{comment.news_title}</div>
{replies.length > 0 && (
<div className="replies">
{replies.map(reply => (
<Comment comment={reply} key={reply.news_id} replies={[]}/>
))}
</div>
)}
</div>
</div>
)
}
This is an example on how the comment structure would look like:
Comment 1
Reply 1.1
Reply 1.1.1
Reply 1.1.1.1
Reply 1.2
Comment 2
An idea on how I could render infinite replies, or possibly set the maximum level of replies allowed? Thank you
You just need a little change to the Comment component to recursively render the already nested data structure:
function Comment({ comment, newsList }) {
const replies = newsList.filter((newList) => newList.reply_of === comment.news_id);
return (
<div className="comment">
<div className="comment-image-container">
<img src="/user-icon.png" />
</div>
<div className="comment-right-part">
<div className="comment-content">
<div className="comment-author">{comment.user_name}</div>
<div>
{comment.day}, {moment(comment.date).format("DD-MM-YYYY")} at{" "}
{comment.time}
</div>
</div>
<div className="comment-text">{comment.news_title}</div>
{replies.length > 0 && (
<div className="replies">
{replies.map((reply) => (
<Comment key={reply.news_id} comment={reply} newsList={newsList} />
))}
</div>
)}
</div>
</div>
);
}
Basically, you just move the code that gets the direct replies to a comment into the Comment component.
When you render the root Comment component, all direct replies to the root comment will be identified and will cause the rendering of nested Comment components, which will in turn identify the replies to the reply, render a nested Comment component and so on.
Trying to use nested foreach in React.
But not working at all;
return (
{category.map((item, i) => (
<div className="col-sm-12">
<h5>{item.title}</h5>
</div>
item.data.map((it, iv) => (
<div class="col-6">
<ProductCard {...it} />
</div>
))
))}
)
You are using .map higher order function
you need to wrap in in fragment so React Will know that it's a JSX or wrap it on div
Introduction to JSX
Learn Fragment React
return (
<>
{category.map((item, i) => (
<div className="col-sm-12">
<h5>{item.title}</h5>
</div>
item.data.map((it, iv) => (
<div class="col-6">
<ProductCard {...it} />
</div>
))
))}
</>
)
if you want to render nested array maybe it will work
return (
<> // Put Wrapper
{category.map((item, i) => (
<> // put an empty fragment to contain title div and another map
<div key={i} className="col-sm-12"> // dont forget to put key props
<h5>{item.title}</h5>
</div>
{item.data.map((it, iv) => ( // wrap another map on '{}' because it inside jsx
<div key={iv} class="col-6"> // dont forget to put key props
<ProductCard {...it} />
</div>
)}
</>
)}
</>
)
you can also change <> to div or any if you want to style it. And dont put // comment on jsx, it just my mark to explain this code
What I`m doing wrong?It also says: "Check the render method of Card" , which is here:
<div className="grid-container">
{pokemonData.map((pokemon, i) => {
console.log(pokemon.id) // unique numbers are here
return <Card key={pokemon.id} pokemon={pokemon} />
})}
</div>
Card component itself:
function Card({ pokemon }) {
return (
<div className="card">
<div className="card__image">
<img src={pokemon.sprites.front_default} alt="Pokemon" />
</div>
<div className="card__name">
{pokemon.name}
</div>
<div className="card__types">
{
pokemon.types.map(type => {
return (
<div className="card__type" style={{backgroundColor: typeColors[type.type.name]}}>
{type.type.name}
</div>
)
})
}
</div>
<div className="card__info">
<div className="card__data card__data--weight">
<p className="title">Weight:</p>
<p>{pokemon.weight}</p>
</div>
<div className="card__data card__data--height">
<p className="title">Height:</p>
<p>{pokemon.height}</p>
</div>
<div className="card__data card__data--ability">
<p className="title">Abilities:</p>
{/* {console.log(pokemon.abilities)} Temporary for dev puprose */}
{pokemon.abilities.map(ability => <p>{ability.ability.name}</p>
)}
</div>
</div>
</div>
);
}
export default Card;
You can use the index of the array may be your data is having some kind of duplicate. It is recommended that you pass a key prop whenever you are returning a list.
<div className="grid-container">
{pokemonData.map((pokemon, i) => {
console.log(pokemon.id) // unique numbers are here
return <Card key={i} pokemon={pokemon} />
})}
</div>
Equally, check this segment of card components.
{
pokemon.types.map((type,i) => {
return (
<div key={i} className="card__type" style={{backgroundColor:
typeColors[type.type.name]}}>
{type.type.name}
/div>
)
})
}
And
<div className="card__data card__data--ability">
<p className="title">Abilities:</p>
{/* {console.log(pokemon.abilities)} }
{pokemon.abilities.map((ability, i) => <p key={i}>{ability.ability.name}
</p>
)}
</div>
Previous answer will solve your problem. However, for your info, I would also like to add here.
For React a key attribute is like an identity of a node/element/tag which helps React to identify each item in the list and apply reconciliation correctlyon each item. Without a key React will render your component but may cause issue when you re-order your list.
React recommends to use id of the data instead of index number. However, if your list does not re-orders/ sorts or do not have id then you can use index.
You can read more here:
https://reactjs.org/docs/lists-and-keys.html
Change this:
<div className="card__types">
{
pokemon.types.map(type => {
return (
<div className="card__type"
style={{backgroundColor:typeColors[type.type.name]}}
>
{type.type.name}
</div>
)
})
}
</div>
to:
<div className="card__types">
{
pokemon.types.map((type, key) => {
return (
<div key={key} className="card__type"
style={{backgroundColor:typeColors[type.type.name]}}
>
{type.type.name}
</div>
)
})
}
</div>
and:
{pokemon.abilities.map(ability => <p>{ability.ability.name}</p>
to:
{pokemon.abilities.map((ability,key) => <p key={key} >{ability.ability.name}</p>
This question already has answers here:
Limit items in a .map loop
(5 answers)
Closed 3 years ago.
I'm importing the spaces object. I only want to render 3 of the Card components which requires attributes from spaces. Imagine a 'featured' section on a website displaying only a few items from a larger list.
With the below code I'm currently rendering all of the results instead of 3, am I using the correct methods? And if so, is the order wrong?
<div className="row">
{spaces.map(item => {
for (var i = 0; i < 2; i++) {
return (
<div className="col-sm-12 col-md-6 col-lg-4" key={item.id}>
<Card
town={item.town}
name={item.name}
net={item.net}
day={item.day}
night={item.night}
dog={item.dog}
parking={item.parking}
image={item.image}
/>
</div>
)
}
})}
</div>
spaces.map will iterate over all elements in spaces and produce a new array, with a new value for each existing item.
The loop inside the map callback is useless since you are returning in the first iteration, terminating the loop immediately.
If you only want to get the first 3 spaces, then you can .slice the array:
<div className="row">
{spaces.slice(0,3).map(item => {
return (
<div className="col-sm-12 col-md-6 col-lg-4" key={item.id}>
<Card
town={item.town}
name={item.name}
net={item.net}
day={item.day}
night={item.night}
dog={item.dog}
parking={item.parking}
image={item.image}
/>
</div>
)
})}
</div>
You can do like below
<div className="row">
{spaces.map((item,index) => {
if(index < 3){
return (
<div className="col-sm-12 col-md-6 col-lg-4" key={item.id}>
<Card
town={item.town}
name={item.name}
net={item.net}
day={item.day}
night={item.night}
dog={item.dog}
parking={item.parking}
image={item.image}
/>
</div>
)
}
})}
</div>
I am trying to nest maps to render an array within an object
My Cards Component Render Method (Not Nested, Working):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map(
(route, index) =>
<Card busName={this.state.routeList[index].$.tag} />
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
My Cards Component Render Method (Nesteing, Not Working!!):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
{
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
}
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
busTitleToDirectionName(int) returns an array of Strings
My Card Subcomponent's render method:
render() {
// Logging to see if render method is called
console.log("Ran");
return (
<div className="card">
<div className="card-header">
<div className="bus-name">
<p>{this.props.busName}</p>
</div>
</div>
</div>
);
}
How it looks like without nesting when it does work (Not enough reputation to post images so here are the links):
https://i.gyazo.com/66414925d60701a316b9f6325c834c12.png
I also log in the Card subcomponent so that we know that the Card component was ran and it logs that it did get called without nesting
https://i.gyazo.com/fb136e555bb3df7497fe9894273bf4d3.png
When nesting, nothing renders and the Card subcomponent isn't being called as there is no logging of it
https://i.gyazo.com/38df248525863b1cf5077d4064b0a15c.png
https://i.gyazo.com/d6bb4fb413dfc9f683b44c88cce04b8a.png
You can try below code in your nested case. In the nesting of map you have to wrap your nested map within a container. Here I use React.Fragment (<> ) as a container.
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
<>
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
</>
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
Hope it will help you!!
Thanks, Prahbat Kumar, but I figured out the issue. I had to return the subcomponent from the nested map here is the code:
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
return <Card busName={busDir} />
})
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}