skip to Main Content

As the title suggest, I’ve got a NextJS component which renders NextUI Dropdowns using a object of data. The problem i’m facing right now is that when i have a dropdown already open and want to open anoter dropdown rendered by the component i must click it twice to get it open (one click for close the already open dropdown and one click for open the dropdown i want to).

The component is for a NextJS project using typescript, tailwind for styles and NextUI react library

enter image description here

This is the code of the component:

'use client'

import React, { useState } from 'react'
import {
  Navbar,
  NavbarContent,
  NavbarItem,
  Link,
  DropdownMenu,
  DropdownItem,
  Dropdown,
  DropdownTrigger,
  Button
} from '@nextui-org/react'

export function NavbarFooter() {
  const items = [
    {
      title: 'Dropdown 1',
      dropdown: [
        { title: 'Subitem 1', path: '/' },
        { title: 'Subitem 2', path: '/' }
      ]
    },
    {
      title: 'Dropdown 2',
      dropdown: [
        { title: 'Subitem 1', path: '/' },
        { title: 'Subitem 2', path: '/' }
      ]
    },
    { title: 'Item 3', path: '/' }
  ]

  const [activeDropdown, setActiveDropdown] = useState<null | number>(null)

  const handleDropdownClick = (index: number | null) => {
    setActiveDropdown((prev) => (prev === index ? null : index))
  }

  return (
    <Navbar
      className="top-[4rem] w-full bg-[#BC9A22] px-0 md:h-[2.8rem]"
      height="0.8rem"
      maxWidth="2xl"
    >
      <NavbarContent justify="end" className="">
        {items.map((item, index) =>
          item.dropdown ? (
            <NavbarItem key={`${item.title}-${index}`}>
              <Dropdown
                isOpen={activeDropdown === index}
                onOpenChange={() => handleDropdownClick(index)}
              >
                <DropdownTrigger>
                  <Button>
                    {item.title}
                    {activeDropdown === index ? ' ▲' : ' ▼'}
                  </Button>
                </DropdownTrigger>

                <DropdownMenu>
                  {item.dropdown.map((subItem, subIndex) => (
                    <DropdownItem key={subIndex}>
                      <Link href={subItem.path}>{subItem.title}</Link>
                    </DropdownItem>
                  ))}
                </DropdownMenu>
              </Dropdown>
            </NavbarItem>
          ) : (
            <NavbarItem key={`${item.title}-${index}`}>
              <Link href={item.path}>{item.title}</Link>
            </NavbarItem>
          )
        )}
      </NavbarContent>
    </Navbar>
  )
}

2

Answers


  1. Maybe try changing the click handler to:

    const handleDropdownClick = (index: number | null) => {
        if(activeDropdown === index) {
            setActiveDropdown(null);
        }else {
            setActiveDropdown(index);
        }
    }
    
    Login or Signup to reply.
  2. You can modify the handleDropdownClick function to set the new active dropdown index directly, without toggling the current one.

    const handleDropdownClick = (index: number) => {
      // Set the active dropdown to the current index
      setActiveDropdown(index);
    };
    

    To close the dropdown, you will need to add an effect that attaches a click event listner to the window, and when a click is detected outside of the dropdown content, the dropdown is closed.

    useEffect(() => {
      const closeDropdown = (event: MouseEvent) => {
        // Cast the event target to an HTMLElement instance
        const target = event.target as HTMLElement;
        // Check if the clicked element is part of a dropdown. If not, close the open dropdown.
        const isDropdown = target.closest('[role="listbox"]') || target.closest('[data-nextui-dropdown-trigger]');
        if (!isDropdown) {
          setActiveDropdown(null);
        }
      };
    
      // Attach the event listener to the window
      window.addEventListener('mousedown', closeDropdown);
    
      // Clean up the event listener when the component is unmounted
      return () => window.removeEventListener('mousedown', closeDropdown);
    }, []);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search