This question already has answers here:
How to Render Nested Map Items inside react component
(2 answers)
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 8 months ago.
So, I have a file data.js where there is an array with some navigation items.
export const footerNavigation = [
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
];
Footer.jsx
import React from 'react';
import { footerNavigation } from '../data/data';
const Footer = () => {
return (
<div>
{footerNavigation.map((item, index) => {
return (
<div key={index}>
<h3 className='text-lg font-bold mb-8'>{item.category}</h3>
<ul>
<li>
</li>
</ul>
</div>
)
})}
</div>
)
}
export default Footer;
The task is to make sure there is an href value inside a link tag like <a href={.href}</a> and the value of the item here <a>{.value}</a>
I have a basic understanding how to map items that are in the initial array, but have no clue how to map the array that is inside an object which is inside the initial array.
You only missing another items iteration:
const Footer = () => {
return (
<div>
{/* category unique acts as key */}
{footerNavigation.map(({ category, items }) => {
return (
<div key={category}>
<h3 className="text-lg font-bold mb-8">{category}</h3>
<ul>
{/* text unique should be a key */}
{items.map(({ href, text }) => (
<a key={text} href={href}>
{text}
</a>
))}
</ul>
</div>
);
})}
</div>
);
};
Related
This question already has answers here:
Can I add a key prop to a React fragment?
(3 answers)
Closed 7 months ago.
I'm trying to build a nav menu that gets its data (links, and link names) from an array called navMenuItems. navMenuItems has an array within it for child links as I'm trying to build a menu similar to the mobile https://www.w3schools.com/ one. I'm running into the error "Each child in a list should have a unique "key" prop.", I'm not sure where I should be adding the key prop to, but I have a feeling its this section (full code below):
<div>
{item.childMenuItems.map((childItem) => {
<Link
key={childItem.name.replace(' ', '')}
href={childItem.url}
>
{childItem.name}
</Link>;
})}
</div>
Array:
const navMenuItems = [
{ name: 'Home', url: '#' },
{
name: 'Skincare Goal',
url: '#',
childMenuItems: [
{ name: 'Anti-Aging', url: '/categories/anti-aging' },
{ name: 'Wound Healing', url: '/categories/wound-healing' },
{ name: 'Acne-Fightning', url: '/categories/acne-fightning' },
{ name: 'Brightening', url: '/categories/brightening' },
{ name: 'UV Protection', url: '/categories/uv-protection' },
],
},
{
name: 'Ingredients',
url: '#',
childMenuItems: [
{ name: 'AHA (All Types', url: '/ingredients/aha' },
{ name: 'Anti-Aging', url: '/ingredients/bha' },
{ name: 'BHA (Salicylic Acid)', url: '/ingredients/anti-aging' },
{ name: 'PHA (All Types)', url: '/ingredients/pha' },
{ name: 'Niacinamide', url: '/ingredients/niacinamide' },
{ name: 'Vitamin A', url: '/ingredients/vitamin-a' },
{ name: 'Hyaluronic Acid', url: '/ingredients/hyaluronic-acid' },
{ name: 'Ceramides', url: '/ingredients/ceramides' },
{ name: 'Azelaic Acid', url: '/ingredients/azelaic-acid' },
],
},
NavBar code snippet:
<ul>
{navMenuItems.map((item) => {
/* Implement child links, check if parent link has childen, if not, use link tag, else use a or button? */
if (
!Array.isArray(item.childMenuItems) ||
!item.childMenuItems.length
) {
return (
<li key={item.name.replace(' ', '')}>
<Link href={item.url}>{item.name}</Link>
</li>
);
} else {
return (
<>
<li key={item.name.replace(' ', '')}>
<Link href={item.url}>{item.name}</Link>
</li>
<div>
{item.childMenuItems.map((childItem) => {
<Link
key={childItem.name.replace(' ', '')}
href={childItem.url}
>
{childItem.name}
</Link>;
})}
</div>
</>
);
}
})}
</ul>
Your problem is this part, here:
} else {
return (
<>
{/* ... everything else ... */}
</>
The <> is actually a react element called a Fragment. Most of the time <> is sufficient. The fragment is the element that needs the key for the first map, not the inner li element. When a key is in a fragment, the full name must be used. So instead, do:
else {
return (
<React.Fragment key={item.name.replace(' ', '')}>
{/* ... everything else ... */}
</React.Fragment>
// while accessing the object values from data, I'm getting undefined in map
// ../data/section1
const data = [{
id: 1,
image: './images/homepage/xbox-games.png',
text: 'Buy Xbox games and consoles',
}, {
id: 2,
image: './images/homepage/shop_surface_devices.webp',
text: 'Shop surface devices',
}, {
id: 3,
image: './images/homepage/choose_your_ms_365.png',
text: 'Choose your Microsoft 365',
}, {
id: 4,
image: './images/homepage/shop_windows_10.png',
text: 'Shop Windows 10',
}]
export default data;
// the actual component
import data from "../data/section1";
const Section1 = () => {
return (
<>
<div class="mx-20">
{data.map((vals) => {
<div class="">
<img src={vals.image}/>
<p>{vals.text}</p>
</div>
})}
</div>
</>
)
}
export default Section1;
return JSX from the map
import data from "../data/section1";
const Section1 = () => {
return (
<>
<div class="mx-20">
{data.map((vals) => {
return (
<div class="">
<img src={vals.image}/>
<p>{vals.text}</p>
</div>
)
})}
</div>
</>
)
}
export default Section1;
I had the same problem, then tried the first bracket instead of the second, and it resolved the problem
import data from "../data/section1";
const Section1 = () => {
return (
<>
<div class="mx-20">
{data.map((vals) => (
<div class="">
<img src={vals.image}/>
<p>{vals.text}</p>
</div>
))}
</div>
</>
)
}
export default Section1;
I am writing some code for a ReactJS component to have an array of chips. I want each chip to be styled uniquely, so I set up a makeStyles class for each one. I was having trouble trying to figure out how to change the class for each tag. This is what I got so far:
const classes = useStyles();
const [chipData, setChipData] = React.useState([
{ key: 0, label: 'Heating' },
{ key: 1, label: 'Printing' },
{ key: 2, label: 'Resetting' },
{ key: 3, label: 'Idle' },
{ key: 4, label: 'Suspended' },
{ key: 5, label: 'Suspend in Progress' },
{ key: 6, label: 'Attention - Printer Connection Lost' },
{ key: 7, label: 'Attention - Filament Out' },
{ key: 8, label: 'Attention - Cooldown Failed' },
]);
return (
<Box display="flex" flexDirection="row" alignItems="flex-start" className={classes.container}>
{chipData.map((data) => {
return (
<div classes={classes.chipContainer}>
<li key={data.key}>
<Chip
label={data.label}
if (label === 'Heating') {
className={classes.heatingTag}
}
/>
</li>
</div>
);
})}
</Box>
);
}
export default PrinterStatusTags
So within the chip element, I have an if statement that is used to assign a specific class based on the label. My plan was to have an if statement for each label, but I am getting the following error:
Parsing error: Unexpected token
Any ideas how I can assign a class based on the chip?
Updated Answer
I would 2 things:
Add a new type property for every chip.
Create a mapper from the type (in the 1st point) to the classes
const classesMapper = {
first: classes.firstClass,
second: classes.secondClass
// ...
};
const [chipData, setChipData] = React.useState([
{ key: 0, label: 'Heating', type: 'first' },
{ key: 1, label: 'Printing', type: 'seocnd' },
// ....
]);
After you have the mapping between every chip to its type. Just render it:
return (
<Box display="flex" flexDirection="row" alignItems="flex-start" className={classes.container}>
{chipData.map((data) => {
return (
<div classes={classes.chipContainer}>
<li key={data.key}>
<Chip
label={data.label}
className={classesMapper[data.type]} />
</li>
</div>
);
})}
</Box>
);
Old Answer
You should write the code a little bit different:
Use className property and not the property class (nor classes)
Set the condition inside the className property. Please note that there are better ways to set the right class but for your case, that would be good enough.
This is the code as it should be:
<div classeName={classes.chipContainer}>
<li key={data.key}>
<Chip label={data.label} className={ label ==== 'Heating' && classes.heatingTag}/>
</li>
</div>
I have an array of objects which I'm rendering by section - see title of each object "Price", "Sectors and Charges" etc.
This populates a mini modal where users can select options to update rendered columns basically a filter.
The selection of the items are working however if I make a selection of the first item "0" all sections with the first option are selected.
How can I store the selection from each object into the selectedOptions array?
Please note I'm using react js and styled components, I've not added the styled component code.
Data:
const columnsData = [
{
title: 'Price',
options: [
{
label: 'Daily Change'
},
{
label: 'Price'
},
{
label: 'Price Date'
},
{
label: 'Volatility Rating'
}
],
},
{
title: 'Sectors and Charges',
options: [
{
label: 'Sector'
},
{
label: 'Asset Class'
},
{
label: 'AMC'
},
],
},
{
title: 'Cumulative Performance',
options: [
{
label: '1 month'
},
{
label: '6 months'
},
{
label: '1 year'
},
],
},
]
Code:
const EditColumns = ({active, onClick}) => {
const [selectedOptions, setSelectedOptions] = useState([0, 1, 2]);
const update = () => {
onClick();
}
const updateSelection = (z) => {
setSelectedOptions(selectedOptions.includes(z) ? selectedOptions.filter(j => j !== z) : [...selectedOptions, z]);
}
return (
<Wrap onClick={() => update()}>
<CTA>
<SVG src="/assets/svgs/btns/edit.svg" />
<span>Columns</span>
</CTA>
{active &&
<Dropdown>
<Head>
<span className="title">Edit Columns</span>
<span>Select the columns you would like to see</span>
</Head>
<Body>
{columnsData.map((item, i) => {
return (
<Section key={i}>
<SectionHead>
<span className="title">{item.title}</span>
<span>Select all</span>
</SectionHead>
<SectionList>
{item.options.map((child, z) => {
const selected = selectedOptions.includes(z);
return (
<li key={z} className={classNames({selected})} onClick={() => updateSelection(z)}>
<span>{child.label}</span>
</li>
)
})}
</SectionList>
</Section>
)
})}
</Body>
</Dropdown>
}
</Wrap>
)
}
export default EditColumns;
Your section lists are all sharing the same state variable, so any changes will be applied to all of them. You could fix this either by constructing a more complex state object which more closely resembles the structure of columnsData, or making each SectionList its own component with its own state. What you decide to do will depend on the degree to which the EditButtons component actually needs access to the whole state.
The second approach might look something like this:
const EditColumns = ({active, onClick}) => {
const update = () => {
onClick();
}
return (
<Wrap onClick={() => update()}>
<CTA>
<SVG src="/assets/svgs/btns/edit.svg" />
<span>Columns</span>
</CTA>
{active &&
<Dropdown>
<Head>
<span className="title">Edit Columns</span>
<span>Select the columns you would like to see</span>
</Head>
<Body>
{columnsData.map((item, i) => {
return (
<Section key={i}>
<SectionHead>
<span className="title">{item.title}</span>
<span>Select all</span>
</SectionHead>
<SectionList options={item.options}/>
</Section>
)
})}
</Body>
</Dropdown>
}
</Wrap>
)
}
const SectionList = ({options}) => {
const [selectedOptions, setSelectedOptions] = useState([0, 1, 2]);
const updateSelection = (z) => {
setSelectedOptions(selectedOptions.includes(z) ? selectedOptions.filter(j => j !== z) : [...selectedOptions, z]);
}
return (
<SectionListContainer>
{options.map((child, z) => {
const selected = selectedOptions.includes(z);
return (
<li key={z} className={classNames({selected})} onClick={() => updateSelection(z)}>
<span>{child.label}</span>
</li>
)
})}
</SectionListContainer>
)
}
Hello I have doubts on how I can do this in react using useState,
basically i have this menu where i need to map, i basically need a state containing all tags, and with boolean state true or false to know if the current item is active, and i will make it active by clicking on the item, and deactivate it when another item is clicked
that is, only one menu item active at a time
export const SideBarTags = [
{
name: 'Tutoriais',
link: '../tutorials',
icon: faFileAlt,
dropdownItems: null,
active: false,
},
{
name: 'Avisos',
link: '../news',
icon: faNewspaper,
dropdownItems: null,
active: false,
},
{
name: 'Serviços',
link: '../services',
icon: faMeteor,
active: false,
dropdownItems: [
{ name: 'Elo Boost', link: '/eloBost' },
{ name: 'Duo Boost', link: '/duoBoost' },
{ name: 'MD10', link: '/eloBost' },
{ name: 'Coaching', link: '/duoBoost' },
{ name: 'Vitóriais', link: '/duoBoost' },
],
},
{
name: 'Carteira',
link: '../cartcredit',
icon: faWallet,
active: false,
dropdownItems: [
{ name: 'Histórico', link: '/history' },
{ name: 'Adicionar Crédito', link: '/add' },
],
},
];
and my TSX:
const MenuTags: React.FC<Hamburguer> = ({ isOpen }) => {
const [menuTags, setMenuTags] = useState(SideBarTags.map());
return (
<DashMenu open={isOpen}>
<MenuItem /> //(this is my tag <li>
</DashMenu>
);
};
const MenuItem: React.FC = () => {
return (
<ListItem>
<ListWrap
>
<a>
<FontAwesomeIcon
className="icon-li"
icon={icon}
size={isOpen ? 'lg' : 'lg'}
fixedWidth
color="white"
/>
<span
className="li-name"
>
{name}
</span>
</a>
</ListItem>
);
};
Component logic if you wanted to map the menu items with the active item
const [menuItems, setMenuItems] = useState(SideBarTags);
const clickHandler = name => () => {
setMenuItems(items =>
items.map(item => ({
...item,
active: item.name === name
}))
);
};
...
{menuItems.map(item => (
<li
key={item.name}
className={item.active ? "active" : ""}
onClick={clickHandler(item.name)}
>
{item.name}
</li>
))}
CSS
.active {
// could be whatever style you need
color: red;
}
Super simplified version of what I did in a past project:
const MenuTags = () => {
const [selectedLink, setSelectedLink] = useState(null)
return (
<ul>
{SideBarTags.map((obj) => (
<li className={`${selectedLink === obj.name ? 'link--selected' : ''}`}>
<a
onClick={() => {
setSelectedLink(obj.name)
}}
href={obj.link}
>
{obj.name}
</a>
</li>
))}
</ul>
)
}
Use CSS to open and close the menu items, by having a class such as 'link--selected' added to an element you can just show that item.