Accessing functionality from parent component in child component ReactJS - javascript

I have working code in App.jsx. Everything is working when this written together in one file.
const App = props => {
const [cartProducts, setCartProducts] = useState([]);
const [products, setProducts] = useState(getProducts());
//const [searchValue, setSearchValue] = useState();
const handleAddProductToCart = productID => {
setCartProducts([...cartProducts, productID]);
};
const handleRemoveFromCart = productID => {
const newCartProducts = cartProducts.filter(id => id !== productID);
setCartProducts(newCartProducts);
};
/*const filterItems = ({ description, title }) => {
return title.toLocaleLowerCase().indexOf(searchValue.toLocaleLowerCase())
|| description.toLocaleLowerCase().indexOf(searchValue.toLocaleLowerCase())
}*/
return (
<>
<Header/>
<Search/>
<Sidebar/>
<div>
{products.map(product => {
const { id, title, description, image } = product;
let haveInCart = false;
cartProducts.forEach(productID => {
if (productID === id) {
haveInCart = true;
}
});
return (
<Card key={id} className="item-card">
<CardImg src={image} alt={title} className="item-img" style={{ height: '260px' }} />
<CardBody style={{ border: 'none' }} className="custom-card-body">
<CardTitle style={{ border: 'none' }} className="custom-card-title">{title}</CardTitle>
<CardText style={{ border: 'none' }} className="custom-card-text">{description}</CardText>
<FontAwesomeIcon icon={faCartPlus} className="add-icon" onClick={() => handleAddProductToCart(id)} />
</CardBody>
</Card>
);
})}
<h2>Your Cart</h2>
{cartProducts.length > 0
? cartProducts.map(productID => {
const productIndex = products.findIndex(product => {
return product.id === productID;
});
let { id, title, image } = products[productIndex];
return (
<Card key={id} className="item-card">
<CardImg src={image} alt={title} className="item-img" style={{ height: '260px' }} />
<CardBody style={{ border: 'none' }} className="custom-card-body">
<CardTitle style={{ border: 'none' }} className="custom-card-title">{title}</CardTitle>
<FontAwesomeIcon icon={faTrash} className="remove-icon" onClick={() => handleRemoveFromCart(id)} />
</CardBody>
</Card>
);
})
: "Yor Cart is Empty :("}
</div>
</>
)
}
export default App;
I want to put product Card and cart Card into it's own component. And when I'm doing it, like this
const Product = ({product}) => {
const { id, title, description, image } = product;
return (
<Card key={id} className="item-card">
<CardImg src={image} alt={title} className="item-img" style={{ height: '260px' }} />
<CardBody style={{ border: 'none' }} className="custom-card-body">
<CardTitle style={{ border: 'none' }} className="custom-card-title">{title}</CardTitle>
<CardText style={{ border: 'none' }} className="custom-card-text">{description}</CardText>
<FontAwesomeIcon icon={faCartPlus} className="add-icon" /*onClick={() => handleAddProductToCart(id)}*/ />
</CardBody>
</Card>
)
}
I'm not getting properties of product in my Card. I want to make my code better organized and save it's functionality. How to access those states from Product and Cart component?
EDIT
Here's the link to codesandbox
https://codesandbox.io/s/late-cookies-r2inh?file=/src/App.jsx&fbclid=IwAR38tcE39tVL51YpG4_6A1HRz-kth1GSIocQWMPrU3QXepc5CHUNn-ZqiG8
EDIT 2 How I can make items in cart be displaying beside Product component, on the left side? Should I create entire Cart component for it?

Your Product.jsx file should look like this:
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCartPlus } from "#fortawesome/free-solid-svg-icons";
import Card from "react-bootstrap/Card";
import CardBody from "react-bootstrap/Card";
import CardImg from "react-bootstrap/CardImg";
import CardTitle from "react-bootstrap/Card";
import CardText from "react-bootstrap/Card";
const Product = ({ product, handleAddProductToCart }) => {
const { id, title, description, image } = product;
return (
<Card key={id} className="item-card">
<CardImg
src={image}
alt={title}
className="item-img"
style={{ height: "260px" }}
/>
<CardBody style={{ border: "none" }} className="custom-card-body">
<CardTitle style={{ border: "none" }} className="custom-card-title">
{title}
</CardTitle>
<CardText style={{ border: "none" }} className="custom-card-text">
{description}
</CardText>
<FontAwesomeIcon
icon={faCartPlus}
className="add-icon"
onClick={() => handleAddProductToCart(id)}
/>
</CardBody>
</Card>
);
};
export default Product;
And in your App.jsx you can map the products array like this:
{products.map((product) => (
<Product
product={product}
key={product.id}
handleAddProductToCart={handleAddProductToCart}
/>
))}
EDIT:
https://codesandbox.io/s/determined-swartz-5q2fo
I edited the codesandbox, i made the Product component accept both add and remove product callback and the "haveInCart" prop. It will decide which icon and which callback to use base on that prop. I'm not sure about this approch tho

Related

Modal renders based on number of children

I have a tree chart in which each node has children nodes. when clicking on a node a drawer opens exactly like a modal. But the problem is that it rerenders based on number of children available on screen like 6 or 5 times. When I click on node it sets this setIsOpen(true) so I think it's it sets this for all nodes that's why it renders more than 1 time. I saw that in inspect. I tried to use use callback and memo but it didn't work.
const Root = (): JSX.Element => {
return (
<div className={styles.card}>
<Flex style={{ marginTop: 25, gap: 8 }} flexCol itemsCenter justifyCenter>
<OrgIcon />
<Text typography="title" weight="bold" color="rgba(6, 6, 43, 0.6)">
Jisr HR
</Text>
</Flex>
</div>
);
};
type ChartProps = {
onSubmit: (vals: any) => void
};
const Chart = (props: ChartProps): JSX.Element => {
const { onSubmit } = props;
return (
<Tree
lineHeight="50px"
lineWidth="1px"
lineColor="rgb(8 15 54 / 24%);"
lineBorderRadius="30px"
label={<Root />}
>
{data.map((d, i) => (
<Node data={d} key={i} onSubmit={onSubmit} />
))}
</Tree>
);
};
export default Chart;
const Node = ({ data, onSubmit }: Props): JSX.Element => {
const [show, setShow] = useState(false);
const [showEmployeeList, setShowEmployeeList] = useState(false);
const count = data.child?.length || 0;
const { setIsOpen, setIsEdit } = useDepartment();
return (
<>
<TreeNode
key={data.name}
label={
<Card
onClick={(): void => setIsOpen(true)}
onClickEmp={(): void => setShowEmployeeList(true)}
onClickEdit={(): void => { setIsOpen(true); setIsEdit(true); }}
name={data.name}
collapse={
<div
className={styles.collapse}
onClick={(): void => setShow(!show)}
>
<Text typography="paragraph14" color="#475467">
{count}
</Text>
<FAICon
icon={`fa-regular ${show ? 'fa-angle-down' : 'fa-angle-up'}`}
color={undefined}
fontSize={undefined}
/>
</div>
}
/>
}
>
{show
? data.child &&
data.child.map((d: any, i: number): JSX.Element =>
<Node data={d} key={i} onSubmit={onSubmit} />)
: null}
</TreeNode>
<AddDepartmentForm
onSubmit={onSubmit}
/>
<DepartmentEmployees
showEmployeeList={showEmployeeList}
setShowEmployeeList={setShowEmployeeList}
/>
</>
);
};
export default Node;
const Card = ({
name,
collapse,
onClick,
onClickEmp,
onClickEdit,
}: ListData): JSX.Element => {
return (
<div
className={styles.card}
>
<div
className={styles.boxUp}
onClick={onClick}
>
<PlusIcon className={styles.hoverPlus} />
</div>
<div
className={styles.card_header}
onClick={onClickEdit}
>
<Flex itemsCenter style={{ gap: 15 }}>
<DepartmentIcon />
<Text typography="paragraph16" color="rgba(6, 6, 43, 0.6)">
{name}
</Text>
</Flex>
</div>
<div style={{ margin: '12px 10px 12px 10px' }}>
<Flex style={{ gap: 8 }} itemsCenter>
<PlusIcon className={styles.plusIcon} />
<Text typography="paragraph16" color="#2353f5">
Assign Manager
</Text>
</Flex>
<Spacer height={10} />
<Flex justifyBetween>
<Badge
label={
<Flex style={{ gap: 7 }}>
<EmployeeIcon />
23
</Flex>
}
size="large"
type="gray"
variant="stroke"
onClick={onClickEmp}
/>
{collapse}
</Flex>
</div>
<div className={styles.boxDown}>
<PlusIcon className={styles.hoverPlus} />
</div>
</div>
);
};
export default Card;

React: Passing state to child component

I am working on a Frontend Mentor project and I am trying to pass the hover status from the parent component (App) to the child component (Listing).
You can see that I have created a state object called hover inside the App component and passed it to the Listing component but when the hover object updates the css style is not applied as it should be on the Typography element inside the Listing component. Or at least there isn't a re-render if it does.
App.js
let [hover, updateHover] = useState(false);
updateHover = () => {
if(hover === false){hover = true; console.log(hover); return(0);}
else{hover = false; console.log(hover); return;}
}
return (
<ThemeProvider theme={ theme }>
<div style={{backgroundColor:'hsl(180, 52%, 96%)',}}>
<div style={{backgroundImage:`url(${headerImage})`, backgroundSize:'cover', height:'10rem'}}></div>
<Box display="flex" justifyContent="center" alignItems="center">
<Box style={{width:'70%', marginTop:'5rem'}}>
<Card style={styles.listing} onMouseEnter={updateHover} onMouseLeave={updateHover}>
<CardActionArea href="#" className="listingHover">
<Listing id="one" hover={hover} />
</CardActionArea>
</Card>
</Box>
</Box>
</div>
</ThemeProvider>
);
}
export default App;
Listing.js
function Listing(props) {
let id = props.id
let hover = props.hover
return (
<React.Fragment>
<Box className="listing" display="flex" sx={{backgroundColor:'#fff', width:'100%', height:'7.3rem'}}>
<Box>
<Typography variant="h4"><Link className={hover ? 'jobTitle': null} href="#" color="secondary" underline="none">{jobs[id].name}</Link></Typography>
</Box>
</Box>
</Box>
</React.Fragment>
)
}
export default Listing
I think your problem here is that you re declared updateHover.
You should change the name and have something like
const [hover, setHover] = useState(false);
const updateHover = () => {
if (!hover) {
console.log(hover);
setHover(true);
return(0)
} else {
setHover(false)
return;
}
}
as an aside why are you returning from the function? do you need the console.log? a cleaner option would be
const [hover, setHover] = useState(false);
const updateHover = () => setHover(!hover) // flips the current value i.e same as if (hover === true) setHover(false) else setHover(true);
return (
<ThemeProvider theme={ theme }>
<div style={{backgroundColor:'hsl(180, 52%, 96%)',}}>
<div style={{backgroundImage:`url(${headerImage})`, backgroundSize:'cover', height:'10rem'}}></div>
<Box display="flex" justifyContent="center" alignItems="center">
<Box style={{width:'70%', marginTop:'5rem'}}>
<Card style={styles.listing} onMouseEnter={updateHover} onMouseLeave={updateHover}>
<CardActionArea href="#" className="listingHover">
<Listing id="one" hover={hover} />
</CardActionArea>
</Card>
</Box>
</Box>
</div>
</ThemeProvider>
);
}
You are wrong
change state in the React.
Try like this.
const [hover, setHover] = useState(false);
updateHover = () => {
setHover(!hover)
}
or
const [hover, setHover] = useState(false);
return (
<ThemeProvider theme={ theme }>
<div style={{backgroundColor:'hsl(180, 52%, 96%)',}}>
<div style={{backgroundImage:`url(${headerImage})`, backgroundSize:'cover', height:'10rem'}}></div>
<Box display="flex" justifyContent="center" alignItems="center">
<Box style={{width:'70%', marginTop:'5rem'}}>
<Card style={styles.listing} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
<CardActionArea href="#" className="listingHover">
<Listing id="one" hover={hover} />
</CardActionArea>
</Card>
</Box>
</Box>
</div>
</ThemeProvider>
);

How to Set Rows in React-Bootstrap Correctly?

how do I set the number of cards per row? I need 4 cards to be in one row of mine.
I have been trying for hours.. can't figure out what i do wrong..
I was getting all sorts of compilation errors when i played around with this.
thanks!
:)
import { React } from 'react';
import { Card, Button, CardGroup } from 'react-bootstrap';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
const MyDishes = props => {
const { dishes } = props;
return (
<CardGroup style={{display: 'flex', flexDirection: 'row'}}>
{dishes.length > 0 &&
dishes.map(d => (
<Row xs={1} md={2} className="g-4">
{Array.from({ length: 4 }).map((_, idx) => (
<Col>
<Card key={d.id} style={{ width: '100em' }} style={{flex: 1}}>
<Card.Img variant="top" src={d.attributes.picture} />
<Card.Body>
<Card.Title>{d.attributes.name}</Card.Title>
<Card.Text>Possibly some text here</Card.Text>
<Link to={`/dishes/${d.id}`}>
<Button variant="primary">Full Recipe</Button>
</Link>
</Card.Body>
</Card>
</Col>
))}
</Row>
</CardGroup>
);
};
const mapStateToProps = state => {
return {
dishes: state.myDishes
};
};
export default connect(mapStateToProps)(MyDishes);
Above bootstrap 5
<Row className="row-cols-4">
...
</Row>
or any version
<Row className="row-cols-4">
{Array.from({ length: 4 }).map((_, idx) => (
<Col className="col-3">
...
</Col>
</Row>

Changing "likes" based on what picture is shown

I am making an app based off Nasa's api that shows a picture or video each day. I have also created a like button that toggles if a person clicks it to like a picture or to toggle it back to default. I created the app in a way where a user can select a date using a calendar component that is through material-ui and when they select a date, it will bring the picture or video from that date. I am trying to figure out how to attach the like button to each picture, so that if they like one picture, they can go to another day and it will let them like another picture. Currently, for example, if I like today's picture, select the calendar and go to yesterday's picture, my like button is still toggled. Here is the code I used for getting the pictures and other information to show:
export default function PictureOfDay(props) {
const [picture, setPicture] = useState([])
const {date} = props;
let selectDate = moment(date, "ddd MMM DD YYYY HH:mm:ss Z-HHmm");
let formatDate = selectDate.format("YYYY-MM-DD");
useEffect(() => {
axios.get(
`https://api.nasa.gov/planetary/apod?api_key=QQpTYaQHDUvPAyVorMgxfKhQEoSQikBYt5WuFCf6&date=${formatDate}`
)
.then((response) => {
console.log(response.data);
setPicture(response.data);
})
.catch((err) => {
console.log(err)
});
}, [formatDate]);
return (
<div className="info">
{props.fetchingData && (
<div className="key spinner">
<Loader type="Puff" color="#800080" height="60" width="60" />
<p>Loading Data</p>
</div>
)}
<PictureInfo
date={picture.date}
title={picture.title}
image={picture.url}
media={picture.media_type}
explanation={picture.explanation}
/>
</div>
);
}
I also have my Like button logic on a different component. Right now, it is not attached to the pictures and I am unsure how to go about doing that:
import React, {useState, useEffect} from "react";
import ToggleHearts from "./ToggleHearts"
import {
Button,
Typography,
} from "#mui/material";
import {makeStyles} from "#mui/styles";
const useStyles = makeStyles((theme) => ({
button: {
...theme.typography.buttons,
fontFamily: "Orbitron",
padding: "0.7em",
fontSize: "1.5em",
width: "3.2em",
height: "3.2em",
background: theme.palette.primary.mainGradient,
border: "2px solid pink",
"&:hover": {
background: theme.palette.secondary.mainGradient,
},
},
}));
const Likes = () => {
const classes = useStyles();
const [liked, setLiked] = useState(false)
const handleChangeHeart = () => {
setLiked((previousHeart) => {
return !previousHeart;
});
};
useEffect(() => {
setLiked(JSON.parse(window.localStorage.getItem("liked")));
}, [])
useEffect(() => {
window.localStorage.setItem('liked', liked);
}, [liked])
return (
<Button
className={classes.button}
variant="contained"
onClick={handleChangeHeart}
>
<Typography style={{ fontFamily: "Orbitron" }}>
<ToggleHearts liked={liked}/>
</Typography>
</Button>
);
}
export default Likes
The Likes component is just attached to my component that renders everything on the page, here is the code to that:
export default function PictureInfo(props) {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const newDate = moment(props.date).format("dddd, MMMM, Do YYYY");
return (
<React.Fragment>
<Grid
container
direction="row"
justifyContent="center"
alignItems="center"
>
<Grid item className={classes.formGridItem}>
<Typography variant="h2">Date: {newDate}</Typography>
</Grid>
<Grid item className={classes.formGridItem}>
<Typography variant="h2">Title of Photo: {props.title}</Typography>
</Grid>
</Grid>
<Grid
container
direction="column"
justifyContent="center"
alignItems="center"
>
<Grid item className={classes.formGridItem}>
{props.media === "video" ? (
<iframe
className={classes.image}
title="video"
src={props.image}
width="800px"
height="600px"
></iframe>
) : (
<img className={classes.image} alt="nasa" src={props.image} />
)}
</Grid>
<Likes />
<Grid item className={classes.formGridItem} align="center">
<Button
className={classes.button}
variant="contained"
onClick={handleOpen}
>
Click here for more information!
</Button>
</Grid>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography
id="modal-modal-title"
style={{
borderBottom: "1px solid pink",
fontFamily: "'Orbitron', sans-serif",
marginBottom: "1em",
fontSize: "1.4em",
}}
>
Explanation of: {props.title}
</Typography>
<Box
style={{
background: "white",
border: "1px solid purple",
padding: "2em",
}}
>
<Typography
id="modal-modal-description"
variant="subtitle2"
style={{ fontSize: "1.2em" }}
>
{props.explanation}
</Typography>
</Box>
</Box>
</Modal>
<footer>
<Typography
variant="subtitle2"
style={{ fontSize: "1rem", color: "black" }}
>
©2021 Charlene Johnson
</Typography>
</footer>
</Grid>
</React.Fragment>
);
}
If i right to understand you
all picture or video posts, must have an unique ID in you data represent, you data for this must look like this for example:
[{
id:'id of post',
likes:[idOfUsers],
...other needed for you data
}]
So, now you can check for likes id of user, and toggle you 'like button', if ID of user exist in this post into a 'likes' array,
or store IDs of posts to 'postsWhichwasLiked':[idsOfPosts],
and then to define which of posts was liked across getting id from this array and then to comparing with ID of post

How to handle multiple menu state with Material-UI Menu component?

I have a dynamically generated set of dropdowns and accordions that populate at render client-side (validated user purchases from db).
I'm running into an error that I'm sure comes from my menu anchorEl not knowing 'which' menu to open using anchorEl. The MUI documentation doesn't really cover multiple dynamic menus, so I'm unsure of how to manage which menu is open
Here is a pic that illustrates my use-case:
As you can see, the menu that gets anchored is actually the last rendered element. Every download button shows the last rendered menu. I've done research and I think I've whittled it down to the anchorEl and open props.
Here is my code. Keep in mind, the data structure is working as intended, so I've omitted it to keep it brief, and because it's coming from firebase, I'd have to completely recreate it here (and I think it's redundant).
The component:
import { useAuth } from '../contexts/AuthContext'
import { Accordion, AccordionSummary, AccordionDetails, Button, ButtonGroup, CircularProgress, ClickAwayListener, Grid, Menu, MenuItem, Typography } from '#material-ui/core'
import { ExpandMore as ExpandMoreIcon } from '#material-ui/icons'
import LoginForm from '../components/LoginForm'
import { motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import { db, functions } from '../firebase'
import styles from '../styles/Account.module.scss'
export default function Account() {
const { currentUser } = useAuth()
const [userPurchases, setUserPurchases] = useState([])
const [anchorEl, setAnchorEl] = useState(null)
const [generatingURL, setGeneratingURL] = useState(false)
function openDownloads(e) {
setAnchorEl(prevState => (e.currentTarget))
}
function handleClose(e) {
setAnchorEl(prevState => null)
}
function generateLink(prefix, variationChoice, pack) {
console.log("pack from generate func", pack)
setGeneratingURL(true)
const variation = variationChoice ? `${variationChoice}/` : ''
console.log('link: ', `edit-elements/${prefix}/${variation}${pack}.zip`)
setGeneratingURL(false)
return
if (pack.downloads_remaining === 0) {
console.error("No more Downloads remaining")
setGeneratingURL(false)
handleClose()
return
}
handleClose()
const genLink = functions.httpsCallable('generatePresignedURL')
genLink({
fileName: pack,
variation: variation,
prefix: prefix
})
.then(res => {
console.log(JSON.stringify(res))
setGeneratingURL(false)
})
.catch(err => {
console.log(JSON.stringify(err))
setGeneratingURL(false)
})
}
useEffect(() => {
if (currentUser !== null) {
const fetchData = async () => {
// Grab user products_owned from customers collection for user UID
const results = await db.collection('customers').doc(currentUser.uid).get()
.then((response) => {
return response.data().products_owned
})
.catch(err => console.log(err))
Object.entries(results).map(([product, fields]) => {
// Grabbing each product document to get meta (title, prefix, image location, etc [so it's always current])
const productDoc = db.collection('products').doc(product).get()
.then(doc => {
const data = doc.data()
const productMeta = {
uid: product,
title: data.title,
main_image: data.main_image,
product_prefix: data.product_prefix,
variations: data.variations
}
// This is where we merge the meta with the customer purchase data for each product
setUserPurchases({
...userPurchases,
[product]: {
...fields,
...productMeta
}
})
})
.catch(err => {
console.error('Error retrieving purchases. Please refresh page to try again. Full error: ', JSON.stringify(err))
})
})
}
return fetchData()
}
}, [currentUser])
if (userPurchases.length === 0) {
return (
<CircularProgress />
)
}
return(
currentUser !== null && userPurchases !== null ?
<>
<p>Welcome, { currentUser.displayName || currentUser.email }!</p>
<Typography variant="h3" style={{marginBottom: '1em'}}>Purchased Products:</Typography>
{ userPurchases && Object.values(userPurchases).map((product) => {
const purchase_date = new Date(product.purchase_date.seconds * 1000).toLocaleDateString()
return (
<motion.div key={product.uid}>
<Accordion style={{backgroundColor: '#efefef'}}>
<AccordionSummary expandIcon={<ExpandMoreIcon style={{fontSize: "calc(2vw + 10px)"}}/>} aria-controls={`${product.title} accordion panel`}>
<Grid container direction="row" alignItems="center">
<Grid item xs={3}><img src={product.main_image} style={{ height: '100%', maxHeight: "200px", width: '100%', maxWidth: '150px' }}/></Grid>
<Grid item xs={6}><Typography variant="h6">{product.title}</Typography></Grid>
<Grid item xs={3}><Typography variant="body2"><b>Purchase Date:</b><br />{purchase_date}</Typography></Grid>
</Grid>
</AccordionSummary>
<AccordionDetails style={{backgroundColor: "#e5e5e5", borderTop: 'solid 6px #5e5e5e', padding: '0px'}}>
<Grid container direction="column" className={styles[`product-grid`]}>
{Object.entries(product.packs).map(([pack, downloads]) => {
// The pack object right now
return (
<Grid key={ `${pack}-container` } container direction="row" alignItems="center" justify="space-between" style={{padding: '2em 1em'}}>
<Grid item xs={4} style={{ textTransform: 'uppercase', backgroundColor: 'transparent' }}><Typography align="left" variant="subtitle2" style={{fontSize: 'calc(.5vw + 10px)'}}>{pack}</Typography></Grid>
<Grid item xs={4} style={{ backgroundColor: 'transparent' }}><Typography variant="subtitle2" style={{fontSize: "calc(.4vw + 10px)"}}>{`Remaining: ${downloads.downloads_remaining}`}</Typography></Grid>
<Grid item xs={4} style={{ backgroundColor: 'transparent' }}>
<ButtonGroup variant="contained" fullWidth >
<Button id={`${pack}-btn`} disabled={generatingURL} onClick={openDownloads} color='primary'>
<Typography variant="button" style={{fontSize: "calc(.4vw + 10px)"}} >{!generatingURL ? 'Downloads' : 'Processing'}</Typography>
</Button>
</ButtonGroup>
<ClickAwayListener key={`${product.product_prefix}-${pack}`} mouseEvent='onMouseDown' onClickAway={handleClose}>
<Menu anchorOrigin={{ vertical: 'top', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }} id={`${product}-variations`} open={Boolean(anchorEl)} anchorEl={anchorEl}>
{product.variations && <MenuItem onClick={() => generateLink(product.product_prefix, null, pack) }>{`Pack - ${pack}`}</MenuItem>}
{product.variations && Object.entries(product.variations).map(([variation, link]) => {
return (
<MenuItem key={`${product.product_prefix}-${variation}-${pack}`} onClick={() => generateLink(product.product_prefix, link, pack)}>{ variation }</MenuItem>
)
})}
</Menu>
</ClickAwayListener>
</Grid>
</Grid>
)}
)}
</Grid>
</AccordionDetails>
</Accordion>
</motion.div>
)
})
}
</>
:
<>
<p>No user Signed in</p>
<LoginForm />
</>
)
}
I think it also bears mentioning that I did check the rendered HTML, and the correct lists are there in order - It's just the last one assuming the state. Thanks in advance, and please let me know if I've missed something, or if I can clarify in any way. :)
i couldn't manage to have a menu dynamic,
instead i used the Collapse Panel example and there i manipulated with a property isOpen on every item of the array.
Check Cards Collapse Example
On the setIsOpen method you can change this bool prop:
const setIsOpen = (argNodeId: string) => {
const founded = tree.find(item => item.nodeId === argNodeId);
const items = [...tree];
if (founded) {
const index = tree.indexOf(founded);
founded.isOpen = !founded.isOpen;
items[index]=founded;
setTree(items);
}
};
<IconButton className={clsx(classes.expand, {
[classes.expandOpen]: node.isOpen,
})}
onClick={()=>setIsOpen(node.nodeId)}
aria-expanded={node.isOpen}
aria-label="show more"
>
<MoreVertIcon />
</IconButton>
</CardActions>
<Collapse in={node.isOpen} timeout="auto" unmountOnExit>
<CardContent>
<MenuItem onClick={handleClose}>{t("print")}</MenuItem>
<MenuItem onClick={handleClose}>{t("commodities_management.linkContainers")}</MenuItem>
<MenuItem onClick={handleClose}>{t("commodities_management.linkDetails")}</MenuItem>
</CardContent>
</Collapse>
I think this is the right solution for this: https://stackoverflow.com/a/59531513, change the anchorEl for every Menu element that you render. :D
This code belongs to TS react if you are using plain JS. Then remove the type.
import Menu from '#mui/material/Menu';
import MenuItem from '#mui/material/MenuItem';
import { useState } from 'react';
import { month } from '../../helper/Utilities';
function Company() {
const [anchorEl, setAnchorEl] = useState<HTMLElement[]>([]);
const handleClose = (event: any, idx: number) => {
let array = [...anchorEl];
array.splice(idx, 1);
setAnchorEl(array);
};
<div>
{month &&
month.map((val: any, ind: number) => {
return (
<div
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient'}
style={{ borderColor: ind === 0 ? '#007B55' : '#919EAB52' }}
>
<Menu
id='demo-positioned-menu'
aria-labelledby='demo-positioned-button'
anchorEl={anchorEl[ind]}
open={anchorEl[ind] ? true : false}
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient' + ind}
onClick={(event) => handleClose(event, ind)}
anchorOrigin={{
vertical: 'top',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<MenuItem
key={val.id + 'w9348w344ndf allBankAndCardAccountOfClient' + ind}
onClick={(event) => handleClose(event, ind)}
style={{
display: ind === 0 ? 'none' : 'inline-block',
}}
>
<span
style={{
marginLeft: '.5em',
color: 'black',
background: 'inherit',
}}
>
Make Primary
</span>
</MenuItem>
<MenuItem onClick={(event) => handleClose(event, ind)}>
<span style={{ marginLeft: '.5em', color: 'black' }}>Edit</span>
</MenuItem>
<MenuItem
onClick={(event) => handleClose(event, ind)}
style={{
display: ind === 0 ? 'none' : 'inline-block',
}}
>
<span style={{ marginLeft: '.5em', color: 'red' }}>Delete</span>
</MenuItem>
</Menu>
</div>
);
})}
</div>;
}
export default Company;

Categories