Gray Singh
Gray Singh

Reputation: 310

Open Specific Menu in ReactJS

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

Answers (2)

Drew Reese
Drew Reese

Reputation: 202618

Issue

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>

Solution

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>
  );
};

Edit open-specific-menu-in-reactjs

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>
  );
};

Edit open-specific-menu-in-reactjs (forked)

Upvotes: 1

juliomrc
juliomrc

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

Related Questions