When I try to select a specific button rendered from a 2d array, it ends up selecting all rendered buttons. Is there a way to render the lower details for one specific button.
<Grid container>
{this.serviceByLangauge.map((cat, id) => (
<Grid
item
xs={4}
key={cat.id}
className={this.props.classes.categoryGroup}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
{cat.name}
</Button>
<div className={this.props.classes.serviceGroup}>
{servicesList.length > 0 ? (
<List>
{servicesList.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
</Grid>
))}
</Grid>
]2
can you show us the data of the 2d array?
edit:
I guess you're not iterating servicesList.
Try this instead:
<Grid container>
{this.serviceByLangauge.map((cat, id) => (
<Grid
item
xs={4}
key={cat.id}
className={this.props.classes.categoryGroup}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
{cat.name}
</Button>
<div className={this.props.classes.serviceGroup}>
{cat.services.length > 0 ? (
<List>
{cat.services.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
</Grid>
))}
</Grid>
From the images and the source code you provided, I think to following is wrong:
You derive the services to show from your state. In your state, you have only one list of services. When clicking a button, you update the state with the services of the button.
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
Then you iterate on this same state for every button:
<div className={this.props.classes.serviceGroup}>
{servicesList.length > 0 ? (
<List>
{servicesList.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
I think you want to iterate over cat instead of serviceList:
<div className={this.props.classes.serviceGroup}>
{cat.length > 0 ? (
<List>
{cat.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
I had to add a condition to make sure that I'm only rendering the div when category id's match
if (this.state.showThis) {
servicesComponent = (
<List className={this.props.classes.serviceGroup}>
{this.state.services.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv[langCode]} />
</ListItem>
))}
</List>
);
} else {
servicesComponent = <div></div>;
}
const categories = servicesAndCategories.map((category, index) => (
<Grid
item
xs={4}
className={this.props.classes.categoryGroup}
key={index}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, category.services, category.id)}
>
{category[langCode]}
</Button>
{this.state.currentCategory === category.id && servicesComponent}
</Grid>
));
``
Related
I'm getting the warning message validateDOMNesting(...): cannot descendant of li
in the console and it shows the error is on the treeview and the timeline I dont know where to fix the code, the below code is facing that warning is there any solution for this?
<Box>
<TreeView >
<Timeline key={comment.id}>
<TimelineItem>
<TimelineSeparator>
<Avatar {...stringAvatar(comment.username) } />{" "}
<TimelineConnector />
</TimelineSeparator>
<TimelineOppositeContent style={{maxWidth: "1px",paddingLeft: "0px",paddingRight: "0px", }}/>
<TimelineContent>
<Stack direction="row" spacing={1}>
<Typography variant="h6" sx={{fontSize:"15px" ,fontWeight:"bold"}}>
{comment.username.charAt(0).toUpperCase() + comment.username.slice(1)}
</Typography>
<Item variant="span" sx={{fontSize:"10px"}}> {createdAt}</Item>
</Stack>
<ReactMarkdown children={comment.body} rehypePlugins={[rehypeRaw]} />
<TreeItem
nodeId="1" label={
<TimelineContent>
{canReply && (<Chip label="Reply" onClick={() => setActiveComment({ id: comment.id, type: "replying", }) }
sx={{fontSize:"10px",mb:1}} size="small" icon={<ReplyIcon /> }/> )}
{isReplying && (
<CommentForm submitLabel="Reply" hasCancelButton handleSubmit={(text) => addComment(text, replyId)} handleCancel={() => { setActiveComment(null);}} />)}
</TimelineContent> } >
<TreeItem
nodeId="2"
label={
<Timeline>
{replies.length > 0 && (
<div>
{replies.map((reply) => (
<Comment
comment={reply}
key={reply.id}
setActiveComment={setActiveComment}
activeComment={activeComment}
addComment={addComment}
parentId={comment.id}
replies={[]}
currentUserId={currentUserId}
/>
))}
</div>
)}
</Timeline>}/>
</TreeItem>
</TimelineContent>
</TimelineItem>
</Timeline>
</TreeView>
</Box>
i'm building eccommerce and in my cart i want to show a modal when user tap on delete icon but my modal always show and delete last element of my cart
how can i resolve this issue
here's my modal component:
const VerticalModal = ({ item, mShow, hide, name }) => {
return (
<Modal
show={mShow}
size="md"
centered
>
<Modal.Header>
<Modal.Title >
<h6>Remove from cart</h6>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p className="text-center">Are you sure you want to remove {name}</p>
</Modal.Body>
<Modal.Footer className="d-flex justify-content-between">
<Button variant="outline-secondary" size="sm" onClick={hide}>
Cancel
</Button>
<Button variant="outline-danger" size="sm" onClick={item}>
Remove
</Button>
</Modal.Footer>
</Modal>
);
};
here's my cart code :
{cart &&
cart.cartItems.length > 0 &&
cart.cartItems.map((item, index) => (
<Card
key={index}
className=" cart-card-magninn cart-card-shadow"
>
<Row className="p-3">
<Col>{item.img}</Col>
<Col>{item.name}</Col>
<Col md={2} xs={2}>
<button
onClick={() => setModalShow(true)}
>
<FontAwesomeIcon icon={faTrashAlt} />
</button>
<VerticalModal
mShow={modalShow}
hide={() => setModalShow(false)}
item={() => removeFromCartHandler(item.prodid)}
name={item.prodname}
/>
</Col>
</Row>
)}
You're creating four modals and whenever, the modal opens, it has the value of the last item. You should move modal out of the map function and store the selected item in a separate state and use that to delete the info.
Initialize the selected item as empty object.
const [selectedItem, setSelectedItem] = useState({})
Then, update the click method on the button.
<button
onClick={() => {
setModalShow(true)
setSelectedItem(item)
}}>
<FontAwesomeIcon icon={faTrashAlt} />
</button>
After that, move the modal code outside of the map function.
{modalShow ?
<VerticalModal
mShow={modalShow}
hide={() => {
setModalShow(false)
setSelectedItem({})
}}
item={() => {
removeFromCartHandler(selectedItem.prodid)
setSelectedItem({})
}
name={selectedItem.prodname}
/>
: null}
Updated code could be something like this
{cart &&
cart.cartItems.length > 0 &&
cart.cartItems.map((item, index) => (
<Card
key={index}
className=" cart-card-magninn cart-card-shadow"
>
<Row className="p-3">
<Col>{item.img}</Col>
<Col>{item.name}</Col>
<Col md={2} xs={2}>
<button
onClick={() => {
setModalShow(true)
setSelectedItem(item)
}}>
<FontAwesomeIcon icon={faTrashAlt} />
</button>
</Col>
</Row>
)}
{modalShow ?
<VerticalModal
mShow={modalShow}
hide={() => {
setModalShow(false)
setSelectedItem({})
}}
item={() => {
removeFromCartHandler(selectedItem.prodid)
setSelectedItem({})
}
name={item.prodname}
/>
: null}
I'm trying to render a component that uses an array of filters to make a list. I pass the array as an argument to the function but it returns an error. The array is not null or undefined because it shows the itens if I log to the console.
Console.log returns:
Here is my code:
const List = (filtrosPorTipo) => {
let filtros = filtrosPorTipo[0]
let valoresDosFiltros = filtrosPorTipo[1]
let lista = (
<List>
{filtros.map((item, index) => {
return (
<>
<ListItem
button
onClick={() => setOpen({ [item]: !open[item] })}
key={item}
>
<ListItemText primary={item} />
{open[item] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[item]} timeout='auto'>
<List component='div' disablePadding>
{valoresDosFiltros[index].map((filtro) => {
return (
<>
<ListItem key={filtro}>
<p>{`${filtro}\n`}</p>
<ListItemSecondaryAction>
<Checkbox
label={filtro}
key={filtro.toString()}
color='primary'
onChange={() => handleChecked(filtro)}
checked={checked[filtro]}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
</>
)
})}
</List>
</Collapse>
</>
)
})}
</List>
)
return lista
}
Error:
Perhaps the component it's trying to render before "filtros" is assigned. It's a common and logical behavior in React.
Try adding a conditional before the .map() call:
{filtros ? filtros.map((item, index) => {
return (
<>
<ListItem
button
onClick={() => setOpen({ [item]: !open[item] })}
key={item}
>
<ListItemText primary={item} />
{open[item] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[item]} timeout='auto'>
<List component='div' disablePadding>
{valoresDosFiltros[index].map((filtro) => {
return (
<>
<ListItem key={filtro}>
<p>{`${filtro}\n`}</p>
<ListItemSecondaryAction>
<Checkbox
label={filtro}
key={filtro.toString()}
color='primary'
onChange={() => handleChecked(filtro)}
checked={checked[filtro]}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
</>
)
})}
</List>
</Collapse>
</>
)
}) : null}
You can map over the values when they are present like so. If you are not planning on displaying a Loading screen in the process of waiting for the data then this would work. Otherwise use a ternary operator like the other answer.
{filtros && filtros.map((item, index) => {
return (
<>
<ListItem
button
onClick={() => setOpen({ [item]: !open[item] })}
key={item}
>
<ListItemText primary={item} />
{open[item] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[item]} timeout='auto'>
<List component='div' disablePadding>
{valoresDosFiltros[index].map((filtro) => {
return (
<>
<ListItem key={filtro}>
<p>{`${filtro}\n`}</p>
<ListItemSecondaryAction>
<Checkbox
label={filtro}
key={filtro.toString()}
color='primary'
onChange={() => handleChecked(filtro)}
checked={checked[filtro]}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
</>
)
})}
</List>
</Collapse>
</>
)
})}
While rendering the component you have to check data in filtrosPorTipo. It has an array value or not. Like below:
const List = (filtrosPorTipo) => {
if (filtrosPorTipo && filtrosPorTipo.length > 0) {
let filtros = filtrosPorTipo[0]
let valoresDosFiltros = filtrosPorTipo[1]
let lista = (
<List>
{filtros.map((item, index) => {
return (
<>
<ListItem
button
onClick={() => setOpen({ [item]: !open[item] })}
key={item}
>
<ListItemText primary={item} />
{open[item] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[item]} timeout='auto'>
<List component='div' disablePadding>
{valoresDosFiltros[index].map((filtro) => {
return (
<>
<ListItem key={filtro}>
<p>{`${filtro}\n`}</p>
<ListItemSecondaryAction>
<Checkbox
label={filtro}
key={filtro.toString()}
color='primary'
onChange={() => handleChecked(filtro)}
checked={checked[filtro]}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
</>
)
})}
</List>
</Collapse>
</>
)
})}
</List>
)
return lista
}
return 'No data available!'
}
I have implemented Material-UI Slide in my project recently and wanted to ask if someone could explain to me why the code works when I write it this way:
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
</Slide>
But fails to compile when I move the section.map inside the slide. I want to animate the section coming in aswell.
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
)}
</Slide>
The code works for every card, except the ones with a modal inside them. A modal contains e.g. text, textinput. When I click on a card with a modal inside of it i get this error:
Thanks for your help!
In the documentation for Slide's children prop you can find:
A single child content element.
⚠️ Needs to be able to hold a ref.
Slide uses React.cloneElement to add a ref and props to the single child. If there are multiple children or if children is an array (even if the array contains only one child), then Slide will get the error you encountered because children.props is not defined and it is trying to reference children.props.style.
Below is a little example to just help better understand the cause of the error:
import React from "react";
import ReactDOM from "react-dom";
const MockSlide = ({ children }) => {
if (children.props) {
return (
<div>
{children}
children.props is defined
</div>
);
}
return (
<div>
{children}
children.props is not defined
</div>
);
};
const sectionArray = [
"An array also causes problems (even if only one element)"
];
function App() {
return (
<div className="App">
<MockSlide>
<div>Single child works fine</div>
</MockSlide>
<br />
<br />
<MockSlide>
<div>Multiple children</div>
<div>causes problems with Slide</div>
</MockSlide>
<br />
<br />
<MockSlide>
{sectionArray.map(section => {
return <div>{section}</div>;
})}
</MockSlide>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
One potential solution is to wrap everything inside the Slide in a single <div> element.
When I use a few icon menus than box-shadow looks very dark. How to fix that?
1:
Codesandbox example https://codesandbox.io/embed/flamboyant-tdd-r83u1
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</Fragment>
);
})}
</div>
Because, actually you are triggering multiple menus with the same flag at the same time. So shadow is dark because there are multiple menus one after the other.
Below code should fix this, You don't have to render Menu in items loop
render() {
const items = [...Array(10).keys()];
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
</Fragment>
);
})}
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</div>
);
}