Is there any method for map function to only execute Dropdown menu for one cart.For now if i click on one Menu icon , every dropdown appears for every Card in map.
const [isOpen, setIsOpen] = useState(false);
{albums.map((album, i) => (
<Card width="23rem" height="16rem" bckImg={album.bckImgUrl} key={i}>
<AlbumtTitle color={album.color}>{album.name}</AlbumtTitle>
<LinkButton background={album.color} to={`/albums/${album._id}`}>
See more
</LinkButton>
<KebabMenu onClick={() => setIsOpen(!isOpen)} open={isOpen}>
{isOpen ? (
<Dropdown
ref={ref}
style={{
width: "50px",
height: "50px",
}}
>
<li>
<Link to={`editAlbum/${album._id}`}>Edit</Link>
</li>
<hr />
<li>Delete</li>
</Dropdown>
) : (
<Dots />
)}
</KebabMenu>
</Card>
))}
Your isOpen state can be an object that tracks the open state per item in the album:
const [isOpen, setIsOpen] = useState({});
{albums.map((album, i) => (
<Card width="23rem" height="16rem" bckImg={album.bckImgUrl} key={i}>
<AlbumtTitle color={album.color}>{album.name}</AlbumtTitle>
<LinkButton background={album.color} to={`/albums/${album._id}`}>
See more
</LinkButton>
<KebabMenu
onClick={() =>
setIsOpen({ ...isOpen, [album._id]: !isOpen[album._id] })
}
open={!!isOpen[album._id]}
>
{isOpen[album._id] ? (
<Dropdown
ref={ref}
style={{
width: "50px",
height: "50px",
}}
>
<li>
<Link to={`editAlbum/${album._id}`}>Edit</Link>
</li>
<hr />
<li>Delete</li>
</Dropdown>
) : (
<Dots />
)}
</KebabMenu>
</Card>
))}
Instead of
{albums.map((album, i) => (...your code...))}
you need to check whether you have at least one item in albums, and if yes, process it:
{if (albums.length > 0) {
return albums.slice(0,1).map((album, i) => (...your code...))
} else {
return null // or any other actions you want to take in case there are no albums
}
}
To clarify, we check the length of the albums, if it is not empty. If it is, we take only the first element and process it.
Related
i am building a ecommerce web app with react and i am unable to set state profileOptionsLayer when i click on the highlighted listitem both the logs are displayed on the console but component dosent rerender and state "profileOptionsLayer" is not updated either ,i am unable to locate the reason need help!
ALL imports .....
const TemporaryDrawer = ({
profileDrawerlayer,
setprofileDrawerlayer,
setuserDetails,
userDetails,
}) => {
const [profileOptionsLayer, setprofileOptionsLayer] = useState();
console.log(profileOptionsLayer);
return (
<>
<Drawer
anchor={"right"}
open={profileDrawerlayer}
onClose={() => {
setprofileDrawerlayer(false);
}}
>
<Box
sx={{ width: 250, background: "lightblue", height: "100%" }}
role="presentation"
onClick={() => {
setprofileDrawerlayer(false);
}}
onKeyDown={() => {
setprofileDrawerlayer(false);
}}
>
<List>
////////////////////////////////////// Below ///////////////////////////////////////////////
<ListItem disablePadding>
<ListItemButton
onClick={() => {
console.log("dsadsa");
setprofileOptionsLayer("View"); <= unable to set this state
console.log(profileOptionsLayer);
console.log("dsadsa");
}}
>
<ListItemIcon>
<VisibilityIcon />
</ListItemIcon>
<ListItemText primary={"View Profile"} />
</ListItemButton>
</ListItem>
/////////////////////////////////////// UP /////////////////////////////////////////
<ListItem
disablePadding
onClick={() => {
localStorage.removeItem("userId");
setuserDetails("");
}}
>
<ListItemButton>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary={"Logout"} />
</ListItemButton>
</ListItem>
</List>
<Divider />
</Box>
</Drawer>
{profileOptionsLayer &&<ProfileOptions {...{ userDetails }} />}
</>
);
};
export default TemporaryDrawer;
I have a component which i have imported into another component. When I click the component, the onclick function refuses to run.
This is the code for the component
const HomeFeedCard = ({ name, image, published, quantity, weight }) => {
const classes = useHomeFeedCardStyles()
return (
<Grid item xs={6} md={3} sm={4} lg={3}>
<Paper elevation={5}>
<img src={image?.url} alt='Product' className={classes.image} />
<div className={classes.container}>
<Avatar
alt='image'
src='https//user.png'
sx={{ width: 56, height: 56, mr: 1 }}
/>
<div className={classes.textContainer}>
<Typography variant='h6' component='body'>
<LinesEllipsis text={` ${name}`} />
</Typography>
<Typography>{`${quantity} left | ${weight}KG`}</Typography>
</div>
</div>
</Paper>
</Grid>
)
}
THIS IS THE function that runs the onClick function. The console returns nothing
<HomeFeedCard
key={product._id}
name={product.productName}
published={product.published}
image={product.images[0]}
quantity={product.quantity}
weight={product.weight}
onClick={() => console.log('product', product.productName)}
/>
Your onClick is not defined in your function HomeFeedCard.
const HomeFeedCard = ({ name, image, published, quantity, weight, onClick }) => {
return (
<div onClick={onClick} />
)
}
If you don't know which properties you want to set use: https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
In the HomeFeedCard, you are not expecting an onClick function as a prop.
You need to explicitly bind the onClick to an actual element. You can wrap content inside HomeFeedCardwith adiv`.
OR, pass onClick further to Grid if accepts a click handler.
const HomeFeedCard = ({
name,
image,
published,
quantity,
weight,
onClick
}) => {
const classes = useHomeFeedCardStyles();
return (
<div onClick={onClick}>
<Grid item xs={6} md={3} sm={4} lg={3}>
<Paper elevation={5}>
<img src={image?.url} alt="Product" className={classes.image} />
<div className={classes.container}>
<Avatar
alt="image"
src="https//user.png"
sx={{ width: 56, height: 56, mr: 1 }}
/>
<div className={classes.textContainer}>
<Typography variant="h6" component="body">
<LinesEllipsis text={` ${name}`} />
</Typography>
<Typography>{`${quantity} left | ${weight}KG`}</Typography>
</div>
</div>
</Paper>
</Grid>
</div>
);
};
<HomeFeedCard
key={product._id}
name={product.productName}
published={product.published}
image={product.images[0]}
quantity={product.quantity}
weight={product.weight}
onClick={() => console.log("product", product.productName)}
/>
I only want to show the display block on the hovered item. but when I hover it shows on every item in a map function. what I'm doing wrong.
basically, I just want to show hovered movie item's title. for now, it shows every movie when I hover.
MovieList.js
const [popular, setPopular] = useState([]);
const [hover, setHover] = useState(false);
return (
<>
<div className="movie-list">
<h2 style={{ fontSize: "49px", marginLeft: "60px" }}>What's Popular</h2>
<div className="popular">
{popular.map((pop, index) => (
<>
<div className="movie" key={index}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4
id="pop-title"
style={{ display: hover ? "block" : "none" }}
key={index}
>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</>
))}
</div>
</div>
</>
);
};
export default MovieList;
The same hover state variable is used for all your movies, so when it becomes true, all the movies are affected by the change. You could use something like an object instead of just a boolean to store one hover state per movie.
Another problem with your code that isn't helping:
You are missing a unique key prop on each item of the map (it must be on the direct child).
Solution 1: Remove the Fragment
Everything is already under one div so you don't need the React Fragment (<>) in that case. Also, you might wanna use something unique to the current map item other than the index in the array.
{popular.map((pop) => (
<div className="movie" key={pop.somethingUnique}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
))}
Solution 2: Set the key on the Fragment
{popular.map((pop) => (
<Fragment key={pop.somethingUnique}>
<div className="movie">
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</Fragment>
))}
I have a list I want an x icon to appear when user hovers over the item. Currently when user hovers over 1 item the x icon appears on all items in the list. How can I identify just 1 element from the list?
component
const [delWatchlist, setDelWatchlist] = useState(false);
<UncontrolledDropdown isOpen={dropdownOpen} toggle={toggle} className="watchlist-dropdown">
<DropdownToggle caret>
{selected}
</DropdownToggle>
<DropdownMenu>
<DropdownItem onClick={() => createForm()}>Create new list</DropdownItem>
<DropdownItem onClick={() => renameForm()}>Rename</DropdownItem>
<DropdownItem onClick={() => clearForm()}>Clear list</DropdownItem>
<DropdownItem divider />
{watchlists.map((value, index) => (
<DropdownItem value={index} key={value} onClick={() => updateSelected(index)} onMouseEnter={() => setDelWatchlist(true)} onMouseLeave={() => setDelWatchlist(false)}>
<span className="title-watchlists">
{value}
</span>
{delWatchlist && (
<i className="mdi mdi-close-box-outline icon-watchlists" />
)}
</DropdownItem>
))}
</DropdownMenu>
</UncontrolledDropdown>
Use an identifier in delWatchlist.
You could use value, if it is unique. index works, too.
const [delWatchlist, setDelWatchlist] = useState(undefined);
// ...
{watchlists.map((value, index) => (
<DropdownItem
value={index}
key={value}
onClick={() => updateSelected(index)}
onMouseEnter={() => setDelWatchlist(index)}
onMouseLeave={() => setDelWatchlist(current => current === index ? undefined : current)}> // Make sure to only unset itself
<span className="title-watchlists">{value}</span>
{delWatchlist === index && (
<i className="mdi mdi-close-box-outline icon-watchlists" />
)}
</DropdownItem>
))}
I am in the process of making a comment system like the one on youtube. In my implementation, when I click on modify, all comments are now inputs but only the value of the selected input will be modified. how to trigger only the element i clicked.
as you can see it triggers all the array elements
function App() {
const [open, setOpen] = useState(false);
return (
<div className="container mt-5">
<MDBRow>
{data &&
data.map((item) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
<button
onClick={() => {
setOpen(true);
}}
>
Modifier
</button>
</div>
</>
)}
{open && (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
)}
</MDBCol>
))}
</MDBRow>
</div>
);
}
export const UpdateData = ({ name, id, onAbort, submit }) => {
const formik = useFormik({
initialValues: {
id: id,
name: name,
},
onSubmit: async (values) => {
console.log(values);
submit();
},
});
return (
<form onSubmit={formik.handleSubmit}>
<MDBInput
value={formik.values.name}
name="name"
onChange={formik.handleChange}
/>
<div className="float-right">
<span onClick={onAbort} className="text-capitalize grey-text">
Cancel
</span>
<button type="submit">confirm</button>
</div>
</form>
);
};
And this is the sandbox
that i have created
To trigger only one element to be clicked you have to pass the index
function App() {
const [open, setOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState(undefined);
const onSelectedRow = (index) => {
setSelectedRow(index);
setOpen(true);
}
return (
<div className="container mt-5">
<MDBRow>
{data &&
// here you will get the index
data.map((item,index) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
// Now onClick pass the index of selected row to onSelectedRow
<button
onClick={() =>onSelectedRow(index)}
>
Modifier
</button>
</div>
</>
)}
// here add condition to open selected row
{ (open === true && selectedRow === index) ? (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
) : null
}
</MDBCol>
))}
</MDBRow>
</div>
);
}
Sandbox code https://codesandbox.io/s/youthful-wave-k4eih?file=/src/App.js
If you have any queries comment below!
instead of having a false default value in your hook you should have a unique key for each element. By default, it applies to all elements.