Reputation: 310
I have several products that has a button on every product. Once click it would open a specific dropdown menu with its corresponding name. My problem that once I click a product. It opens all dropdowns. CLICK HERE
<div>
{products.map((value) => (
<div>
<DropdownMenu show={isComponentVisible} ref={ref}>
{value.name}
</DropdownMenu>
<div key={value.id}>{value.name}</div>
<button type="button" onClick={() => toggle(value.id)}>
Open
</button>
</div>
))}
</div>
Upvotes: 2
Views: 854
Reputation: 202618
You are using a single boolean state to toggle the visibility of all the dropdowns.
const {
ref,
isComponentVisible,
setIsComponentVisible
} = useComponentVisible(false); // <-- single state
const toggle = () => {
setIsComponentVisible((prevState) => !prevState); // <-- toggle all
};
...
<DropdownMenu
show={isComponentVisible} // <-- all toggled
ref={ref}
>
{value.name}
</DropdownMenu>
Initialize the state to hold an object of boolean values and use the state updater to toggle a specific one by id.
const Form = () => {
const {
ref,
isComponentVisible,
setIsComponentVisible
} = useComponentVisible(
// Map the products to object of id:boolean pairs
products.reduce(
(visible, { id }) => ({
...visible,
[id]: false
}),
{}
)
);
const toggle = (id) => {
// shallow copy state and toggle the specific id's visibility value
setIsComponentVisible((visible) => ({
...visible,
[id]: !visible[id]
}));
};
return (
<div>
{products.map((value) => (
<div key={value.id}>
<DropdownMenu
show={isComponentVisible[value.id]} // set visible by id
ref={ref}
>
{value.name}
</DropdownMenu>
<div style={{ paddingBottom: "100px" }}>
<div key={value.id}>{value.name}</div>
<button type="button" onClick={() => toggle(value.id)}>
Open
</button>
</div>
</div>
))}
</div>
);
};
If you want to only toggle a single dropdown at a time, use a single nullable state to hold the id of the dropdown you want open.
const Form = () => {
const {
ref,
isComponentVisible,
setIsComponentVisible
} = useComponentVisible(null); // <-- null == none open
const toggle = (id) => {
// toggle back to null or set new id
setIsComponentVisible((visible) => (visible === id ? null : id));
};
return (
<div>
{products.map((value) => (
<div key={value.id}>
<DropdownMenu
show={isComponentVisible === value.id} // <-- check if id matches
ref={ref}
>
{value.name}
</DropdownMenu>
<div style={{ paddingBottom: "100px" }}>
<div key={value.id}>{value.name}</div>
<button type="button" onClick={() => toggle(value.id)}>
Open
</button>
</div>
</div>
))}
</div>
);
};
Upvotes: 1
Reputation: 626
When you do the click, you pass the specific id of the dropwdown <button type="button" onClick={() => toggle(value.id)}>
, but on your toggle method, you completely disregard it:
const toggle = (/* Your id should come here */) => {
setIsComponentVisible((prevState) => !prevState);
};
Instead of storing a boolean, you have to store the id of the open menu (or list of ids, if multiple menus can be open at the same time).
Upvotes: 0