React Component requires a unique key prop - reactjs

I made an create component that renders everything that looks good, but when I do console in browser, it has below error.
index.js:2178 Warning: Each child in an array or iterator should have
a unique "key" prop.
I tried to have the key props but still has this error. Please see my code below and advise! Thank you very much.
const serviceList = (props) => (
<CardBody style={{ backgroundImage: `url(${ServiceBgImg})` }}>
<div>
<h3 style={{ fontWeight: 'bold' }}
>{props.groupName}</h3>
{props.allServices.filter(groupedServices => groupedServices.category === props.groupName)
.map(serviceInfo =>
<List component='nav'>
<ListItem>
<ListItemText primary={
<div key={serviceInfo.id}>
<h5 style={{ fontWeight: 'bold' }}>
<span>{serviceInfo.service}</span>
<span
style={{ float: 'right' }}
>{serviceInfo.price}</span>
</h5>
<h5>
{serviceInfo.description}
</h5>
</div>
}
/>
</ListItem>
</List>
)
}
</div>
</CardBody>
);
export default serviceList;

Your List component should wrap the map function, and inside that you add the key to mapped ListItems:
<List component='nav'>
...
.map((serviceInfo, index) =>
<ListItem key={index}>
...
</ListItem>
....

The outermost/parent element returned by the map function needs to have a key prop. In your case, it is not the <div> but <List>, which seems to be a mistake as it seems you wanted to loop over a filtered result of allServices to create a list of serviceInfo. If that's the case, you should move the map function just above the <ListItem> and assign key prop to it.
Code example as follow:
const serviceList = (props) => (
<div>
<h3 style={{ fontWeight: 'bold' }}>{props.groupName}</h3>
<List component='nav'>
{props.allServices.filter(groupedServices => groupedServices.category === props.groupName).map(serviceInfo =>
<ListItem key={serviceInfo.id}>
<ListItemText primary={
<div key={serviceInfo.id}>
<h5 style={{ fontWeight: 'bold' }}>
<span>{serviceInfo.service}</span>
<span
style={{ float: 'right' }}
>{serviceInfo.price}</span>
</h5>
<h5>{serviceInfo.description}</h5>
</div>
}/>
</ListItem>)}
</List>
</div>);
export default serviceList;

Related

How to swap MUI icon on IconButton when hovering

I have these tabs that have a close button on them, if the content in them has edits then the close icon turns to a circle, similar to visual code.
{tabs.map((tab, index) => {
const child = (
<StyledTab
label={
<span>
{tab.label + ':' + tab.hasEdit}
<IconButton size="small" component="span" onClick={() => closeClickHandler(tab.value)}>
{tab.hasEdit ? (
<CircleIcon style={{ fontSize: "12px" }} />
) : (
<CloseIcon style={{ fontSize: "18px" }} />
)}
</IconButton>
</span>
}
value={tab.value}
key={index}
/>
);
return (
<DraggableTab
label={
<span>
{tab.label}
<IconButton size="small" component="span" onClick={() => {
closeClickHandler(tab.value);
}}>
{tab.hasEdit ? (
<CircleIcon style={{ fontSize: "12px" }} />
) : (
<CloseIcon style={{ fontSize: "18px" }} />
)}
</IconButton>
</span>
}
value={tab.value}
index={index}
key={index}
child={child}
/>
);
})}
What I'm having trouble with is getting the icon to change from a circle to a close icon while hovering over the button.
Could someone give me a hand on a good way to implement this please?!
You could do this by adding a state for the items. Then add a onMouseEnter and onMouseLeave events on the IconButton. When hovering we can add the index to the array and finally remove when we're leaving. To determine if a icon needs to change we can check if the index in in the hoveringItems.
const [hoveringItems, setHoveringItems] = useState([]);
function handleHover(index, isLeaving) {
setHoveringItems((prevItems) => {
if (isLeaving) return prevItems.filter((item) => item !== index);
return [...prevItems, index];
});
}
return (
<IconButton
size="small"
component="span"
onClick={() => {
closeClickHandler(tab.value);
}}
onMouseEnter={() => handleHover(index, false)}
onMouseLeave={() => handleHover(index, true)}
>
{tab.hasEdit || hoveringItems.includes(index) ? (
<CircleIcon style={{ fontSize: "12px" }} />
) : (
<CloseIcon style={{ fontSize: "18px" }} />
)}
</IconButton>
);

How to close only one Div at a time in React?

Code:-
const [Close, setClose] = useState(true)
<div className="allDivs">
{item.map((item, index) => {
// console.log("myDivs", myDivs);
return (
<Fragment key={index} >
<div className="tableHeaderBody" id="CLOSEDIV" style={{display: Close ? 'initial' : 'none'}}>
<div className="TableText">
<div className="TableTextHide"></div> <div style={{ color: "white" }} id="SHOW">{item.val}</div></div>
<div className="CloseIcon" id="CloseBtn"><FaCircle style={{ color: "#FC0000", width: "10px", height: "10px", alignItems: "right" }} onClick={() => setClose(false)} /></div>
</div>
</Fragment>
)
})
}
</div>
I want that when i click the Red circle at any div (show in image) it close the div, but right now when i click the One div red circle its closes all the div
please help.
Try this:
Create a new component ChildComponent:
export default function ChildComponent({item}) {
const [Close, setClose] = useState(true) // Every Child now has it's own setClose controll
return (
<Fragment>
<div className="tableHeaderBody" id="CLOSEDIV" style={{display: Close ? 'initial' : 'none'}}>
<div className="TableText">
<div className="TableTextHide"></div>
<div style={{ color: "white" }} id="SHOW">{item.val}</div>
</div>
<div className="CloseIcon" id="CloseBtn">
<FaCircle style={{ color: "#FC0000", width: "10px", height: "10px", alignItems: "right" }} onClick={() => setClose(false)} />
</div>
</div>
</Fragment>
)
}
Pass the ChildComponent to your component shown above:
<div className="allDivs">
{item.map((item, index) => (
<div key={index}>
<ChildComponent item={item} />
</div>
))}
</div>

MaterialUI TypeScript: Is it possible to add multi-level navigation menu to "NavItem"?

How to create multi-level navigation menu with MaterialUI and TypeScript?
I would like to add to the '/questions' the follwing:
2 navigation menu:
/questions/Tags
/questions/Users
like as in the Screenshot
export function NavBar(...) {
return (
<>
<Drawer>
<List>
<NavItem
to="/questions"
primary="questions"
icon={CloudOff}
/>
)}
<NavItem to="/questions" primary="questions" icon={Window} />
</List>
</Drawer>
</>
);
}
Lot of posibility...
The best way it's to prepare an array with the menu information and map on, something like this :
const TestArea = ({ params }) => {
const menu = [{ mainMenu: "tata", subMenu: ["toto", "titi"] }];
return (
<>
<Toolbar
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
}}
>
{menu.map((item) => {
return (
<>
<Typography variant="overline">{item.mainMenu}</Typography>
{item.subMenu.map((subItem) => {
return (
<Typography variant="caption" style={{ marginLeft: "40%" }}>
{subItem}
</Typography>
);
})}
</>
);
})}
</Toolbar>
</>
);
};
With this base, you can customize with the component of your choose, render element like link with a path to...
Button color="inherit" component={Link} to="/classic">
Yes, I know, it's plain JSX not TSX, it's just for example.
If you need more information, say me !

React Conditional Rendering to check if something is an empty tag

I have the following code:
{item && item.description && (
<Link
style={{ display: 'block', paddingBottom: '2px' }}
title="View Details"
onClick={() => setIsOpen(!isOpen)}
{...aria}
>
View Details
<Icon icon={isOpen ? 'collapse_arrow' : 'expand_arrow'}
marginLeft="5px"
verticalAlign="bottom"
/>
</Link>
)}
The problem is that item.description could be an empty tag and therefore the condition is met and a Link component is rendered when it actually shouldn't be.
How is the best way I could avoid this case?
Thanks in advance!
You could use a regex to check for empty tags.
function checkForEmptyTags(tags) {
if (!tags) return false;
const EMPTY_TAGS_REGEX = RegExp('<[^>]*>\s*<\/[^>]*>');
return EMPTY_TAGS_REGEX.test(tags);
}
{item && !checkForEmptyTags(item.description) && (
<Link
style={{ display: 'block', paddingBottom: '2px' }}
title="View Details"
onClick={() => setIsOpen(!isOpen)}
{...aria}
>
View Details
<Icon icon={isOpen ? 'collapse_arrow' : 'expand_arrow'}
marginLeft="5px"
verticalAlign="bottom"
/>
</Link>
)}

Passing props to if statement on a Nested Map - React

I'm have a map nested with a map, and the nested map has an if statement. However it's not working because I need to reference a value from the outter mapped element. I'm not sure the proper syntax. I've tried many wrong things, lol.
How do I reference section.name from the outer element in the nested maps's if statement? I'm not getting any errors, it just doesn't work.
<Container>
{this.state.sections.map(section => (
<Accordion className="my-5" key={section.id} id={section.id}>
<Card key={section.id} id={section.id} name={section.name}
style={{ background: this.state.color1, color: this.state.color2 }}>
<Accordion.Toggle as={Card.Header} eventKey={section.id}>
Section {section.id}: {section.name}
</Accordion.Toggle>
<Accordion.Collapse eventKey={section.id}>
<Card.Body style={{ background: 'white', color: 'black' }}>
<ButtonGroup size="sm" className='buttons'>
{this.state.policies.map(policy => {
if(this.state.section === 'Culture'){
return
<PolicyButton key={policy.id} id={policy.id} policy=
{policy.policy} onAddPolicy={this.handleAddPolicy} />
}
})}
</ButtonGroup>
</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
))}
</Container>

Resources