How to map a nested object - javascript

I have to populate the li element with data stored as a JSON object.
With "title" it works simple. But it's not when talking about name's
values. How can I map the subMenu object to get the name?
<ul>
{data.map(({ title, subMenu }) => (
<li className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
*** here I have to put name ***
</button>
</li>
))}
</ul>
JSON object
[
{
"title": "Breeds",
"subMenu": [
{
"id": 1,
"name": "Dog Breeds"
},
{
"id": 2,
"name": "Cat Breeds"
}
]
},
{
"title": "About Pet Adoption",
"subMenu": [
{
"id": 3,
"name": "About Dog Adoption"
},
{
"id": 4,
"name": "About Cat Adoption"
}
]
}
]

As noted in the comments on the accepted answer div elements are not valid children of buttons (related question) and while one should assign a key to mapped elements in React using the index of the iterated array is not always ideal. (see the Docs or related article from T.J.Crowder's comment).
Given that you are mapping a nested list it seems more appropriate to structure it as such. Here using the title as the outer li key (though a more definite unique property would be better) and the subMenu.id as a key for the inner li.
<ul>
{data.map(({ title, subMenu }) => (
<li key={title} className='mobileMenu-body-nav-item'>
<ul>
{subMenu.map(({ id, name }) => (
<li key={id}>
<button className='mobileMenu-body-nav-item-btn'>{name}</button>
</li>
))}
</ul>
</li>
))}
</ul>

You can just call map again, like this:
<ul>
{data.map(({ title, subMenu }) => (
<li className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
{subMenu.map(({ name }) => (<span>{name}</span>))}
</button>
</li>
))}
</ul>
Change the <span> tag to match however you want this content to be rendered.
Also, if this is React, don't forget to set the key prop appropriately when using map:
<ul>
{data.map(({ title, subMenu }) => (
<li key={title} className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
{subMenu.map(({ name }) => (<div key={name}>{name}</div>))}
</button>
</li>
))}
</ul>

Related

Loop inside JSX

I have an object similar to this:
{
id: number,
kids: [{
id: number,
kids: [{
id: number,
kids: []
}]
}]
}
So it has property kids which is an array of kids each of which might have its own array of kids. I need to render original object in a tree view list like this:
<ul>
{object.map(item => (
<li>
<p>{item.value}</p>
{item.kids ?
{item.kids.map(item => (
<ul>
<li>
<p>{item.value}</p>
</li>
</ul>
))}
: null
}
</li>
))}
</ul>
So every item of kids will be a <li></li> element with <ul></ul> inside of it if item.kids isn't empty array.
I could keep going like this but it's pretty messy and more importantly I don't know when exactly kids property will be an empty array. So I need somehow loop over original object and it's properties and do something like
while (kid.kids) {
return {kid.kids.map(kid => (
<li>
<p>{kid.value}</p>
<ul>
kid.kids.map(kid => (
etc
))
</ul>
</li>
}))
}
But I can't understand the better way to loop like this.
This is probably best solved with recursion.
const Kids = ({id, kids}) => {
return {
<li key={id}>
<p>{id}</p>
{kids
? (<ul>{kids.map((kid) => <Kids id={kid.id} kids={kid.kids} />)}</ul>)
: null;
}
</li>
}
};

React JS - data.map can't create list with the data

I got data.js file with some basic info for food like:
export default [
{
"name": "Crock Pot Roast",
"information":[
{
"date":"24 July 2019",
"type": "Main dish",
"difficulty": "Easy",
"time": "~50",
}
],
"ingredients": [
{
"quantity": "1",
"name": " beef roast",
"type": "Meat"
}
],
...
Some more data
...
}
]
I want to create list on react that can get all elements from data.information which function will be correct to use?
I'm getting the data like this:
const getData = data.map(food => {
return (
<div key={food.name}>
<span>{food.name}</span>
<div>
<div>
<img src={food.imageURL} alt={food.name} />
<div>
<ul>{getFoodInformation}</ul>
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
});
but I can't use food.information.map to create list like:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
Any ideas maybe to use another function not map?
You're right that this line is the problem:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
But, .map() is not a problem. Your information data is an array, so you can .map() it with JSX.
Your problem is this:
<div key={info.date}>{info}</div>
info is an object, and React doesn't know how to print an object "beautifully". You want to use the string data (for example, {info.type}).
If you need to dump the full object, use JSON.stringify like this:
<div key={info.date}>{JSON.stringify(info)}</div>
React does not render array/object into a list by default. you have to be explicit in your code.
your first attempt is 'close' to correct, however, you can notice, ingredients and information are both arrays of objects. it means that you have to generate a tag for each index.
I used JSON.stringify to generate a unique key since using an index for key is bad practice.
The code below shows you one of the correct approaches using react.
const data = [
{
name: "Crock Pot Roast",
imageURL: "https://via.placeholder.com/150",
information: [
{
date: "24 July 2019",
type: "Main dish",
difficulty: "Easy",
time: "~50"
}
],
ingredients: [
{
quantity: "1",
name: " beef roast",
type: "Meat"
}
]
}
];
const CardRecipe = ({data}) => {
const { name, imageURL, information, ingredients } = data;
return (
<div key={name}>
<span>{name}</span>
<div>
<div>
<img src={imageURL} alt={name} />
<div>
{information &&
information.map(info => {
const { date, type, difficulty, time } = info;
return (
<ul key={JSON.stringify(info)}>
<li> {date} </li>
<li> {type} </li>
<li> {difficulty} </li>
<li> {time} </li>
</ul>
);
})}
</div>
<div>
{ingredients &&
ingredients.map(ingredient => {
const { quantity, name, type } = ingredient;
return (
<ul key={JSON.stringify(ingredient)}>
<li> {quantity} </li>
<li> {name} </li>
<li> {type} </li>
</ul>
);
})}
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
};
export default function App() {
const first_reciepe = data[0];
return (
<div className="App">
<CardRecipe data={first_reciepe} />
</div>
);
}
Alternatively, you can print any lists by mapping once on the array, and then on each object/key to generate (x) amount of <"li"> tags.
Example:
const DoList = ({ data }) => {
return data.map(list => {
return (
<ul key={list}>
{Object.keys(list).map(key => {
const value = list[key];
return <li key={key}> {value} </li>;
})}
</ul>
);
});
};
Then you would use it as many time as you want like this:
<DoList data={ingredients} />
<DoList data={information} />
<DoList data={...} />

Display and Map Multiple Keys and its object data

I have a JSON file which I need to map and display but I am not sure how to map the structure like this as this is a bit different.
Below is my JSON file:
var data = {
"megamenu": [
{
"name": "level1.2",
"link": "#",
"multilevel": {
"A": [
{
"name": "A-one",
"link": "#"
}
],
"B": [
{
"name": "B-one",
"link": "#"
}
]
}
},
]
}
Map Function:
{data.megamenu.map((menuitem, index) => (
<li key={index}>{Object.entries(menuitem.multilevel).length}</li>
))}
Following is the sample https://stackblitz.com/edit/react-8in6yq
I want to create a structure something like this:
<ul>
<li>level1.2
<ul>
<li>multilevel
<ul>
<li>A
<ul>
<li>A-one</li>
</ul>
</li>
<li>B
<ul>
<li>B-one</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Note: I know I can change the structure to object and not array but this is something I cannot handle and change. So have to manage with this structure.
So why can't you do something like:
const Multilevel = ({multilevel}) => (
<ul>
{Object.keys(multilevel).map(key => (<li key={key}>{key} /* etc*/</li>))}
</ul>
);
{data.megamenu.map((menuitem, index) => (
<li key={menuitem.name}>
{menuitem.name}
<ul>
<li>multilevel
<Multilevel multilevel={menuitem.multilevel}/>
</li>
</ul>
</li>
))}
That's a bit complex, and I advice you to separate your lists to different components to make code readable.
You need to loop through inner multilevels
{data.megamenu.map((menuitem, index) => (
const innnerItemsDOM = menuitem.multilevel.map((item, index) =>{
return <li>
<ul>
<li>item.name</li>
</ul>
</li>
})
return <li> level1.2
{innnerItemsDOM}
</li>
))}

Filtering array and then map array in array

Pretty sure that I am missing something simple but this code has me banging my head.
I have an array in a local json file as
{
"type": [
{
"name": "Accommodation",
"icon": "🏨",
"subcategory": [
"Hotels",
"Motels",
"Bed & Breakfasts"
]
},
{
"name": "Guided Tours",
"icon": "🚌",
"subcategory": [
"Audio & Self Guided",
"Cruises, Sailing & Water",
"Food, Wine & Nightlife"
]
}
]
}
and importing into a React component as
import { type } from './assets/json/company-type.json';
then I am using filter to the name of companyType (which is based on a select value of either Accommodation or Guided Tours. After I map the returned items before map the subcategory
<ul>
{type
.filter(item => {
return item.name === companyType;
})
.map(item => {
item.subcategory.map(subcategory => {
<li key={subcategory}>
<label id={subcategory} htmlFor={subcategory}>
<input
type="radio"
name={subcategory}
value={subcategory}
/>
<span />
{subcategory}
</label>
</li>;
});
})}
</ul>
However, nothing is returned. Yet if I log subcategory I get each returned in the console. What am I missing?
This is because you are not returning from .map() methods.
Use this modified code and it will work as expected
<ul>
{type
.filter(item => {
return item.name === "Accommodation";
})
.map(item => {
return item.subcategory.map(subcategory => {
return (
<li key={subcategory}>
<label id={subcategory} htmlFor={subcategory}>
<input
type="radio"
name={subcategory}
value={subcategory}
/>
<span />
{subcategory}
</label>
</li>
);
});
})}
</ul>
codeSandbox link: https://codesandbox.io/s/4881483j7x

Each child in an array or iterator should have a unique "key" prop in reactjs

I totally confused, because I have mistake in my console and I read reactjs documentation and all tips on stackoverflow, but I can't unterstand what problem is.
I see list of book's titles ({item.volumeInfo.title}), but console has error.
Here is my code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
class BookList extends Component {
renderBook(mainData) {
return(
<ul>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}
render(){
return (
<div className="book-row">
<div className="book-info">
{this.props.book.map(this.renderBook)}
</div>
</div>
);
}
}
function mapStateToProps({book}) {
return {book};
}
export default connect(mapStateToProps)(BookList);
It is part of API response:
{ "kind": "books#volumes",
"totalItems": 288,
"items": [
{
"kind": "books#volume",
"id": "yXlOAQAAQBAJ",
"etag": "CG7f2mQ+7Nk",
"selfLink": "https://www.googleapis.com/books/v1/volumes/yXlOAQAAQBAJ",
"volumeInfo": {
"title": "Nineteenth Century Home Architecture of Iowa City",
"subtitle": "A Silver Anniversary Edition"
I tried to do the next keys:
key={item.etag}, key={i}, key={item.volumeInfo.title}
but error is still here.
Please help.
Thank you so much.
Since you are mapping over book:
{this.props.book.map(this.renderBook)}
the ul also needs a key prop:
renderBook(mainData, bookIdx) {
return(
<ul key={bookIdx}>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}
This is because there will be many ul siblings and React needs to tell the difference (same as with li).
However, it is better (if possible) to use a key that is not the index of the array. So, if book and item have a unique identifier, it would be best to use that.
So, it looks like you have another array outside of the sample data you provided:
[
{ "kind": "books#volumes",
"totalItems": 288,
"items": [
{
Reason is you are using two map, and assigning the key in only one, assign the key to <ul> also, because of that it is giving you the error, Try this:
renderBook(mainData, index) {
return(
<ul key={index}>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}

Categories

Resources