DEV Community

Coder
Coder

Posted on

Dropdown menu doesn't work as expected

I'm going through a navbar tutorial on youtube (https://www.youtube.com/watch?v=IF6k0uZuypA&t=987s) - the link is with the timecode. It should work like at this moment in the vid

calcHeight should do the work but it doesn't. The navbar changes abruptly like at https://www.youtube.com/watch?v=IF6k0uZuypA&t=925s

function DropdownMenu() {

  const [activeMenu, setActiveMenu] = useState("main");
  const [menuHeight, setMenuHeight] = useState(0);

  function calcHeight(el) {
     const height = el.offsetHeight;
     setMenuHeight(height);
  }

  const mainMenuRef = useRef(null);
  const settingsMenuRef = useRef(null);

  function DropdownItem(props) {
    return (
      <a href="#" className="menu-item" onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}>
        <span className="icon-button">{props.leftIcon}</span>

        {props.children}

        <span className="icon-right">{props.rightIcon}</span>
      </a>
    );
  }

  return (
    <div className="dropdown" style={{height: menuHeight}}>
      <CSSTransition 
        in={activeMenu === "main"}
        unmountOnExit
        timeout={500}
        classNames="menu-primary"
        nodeRef={mainMenuRef}
        onEnter={calcHeight}
        >
...
Enter fullscreen mode Exit fullscreen mode

It's a relatively old tutorial, react changed a bit and, as far as I know, we need to now add a ref.

Even after adding dropdownRef it still doesn't work. It messed up the result even more. Now the height of menu stays the same.

function DropdownMenu() {

  const [activeMenu, setActiveMenu] = useState("main");
  const [menuHeight, setMenuHeight] = useState(null);
  const dropdownRef = useRef(null); 


  //function calcHeight(el) {
  //  const height = el.offsetHeight;
  //  setMenuHeight(height);
  //} Used to be like this


  function calcHeight() {
    const height = dropdownRef.current.offsetHeight;
    setMenuHeight(height);
  }

  const mainMenuRef = useRef(null);
  const settingsMenuRef = useRef(null);

  function DropdownItem(props) {
    //...
  )
}
return (
<div className="dropdown" style={{ height: menuHeight }} ref={dropdownRef}>
//...
)
Enter fullscreen mode Exit fullscreen mode

How to fix this? Looking forward to any help. Thanks in advance

https://github.com/fireship-io/229-multi-level-dropdown/tree/master/src - github repo for the tutorial

Part of the CSS code:

.menu-primary-enter {
  position: absolute;
  -webkit-transform: translateX(-110%);
      -ms-transform: translateX(-110%);
          transform: translateX(-110%);
}

.menu-primary-enter-active {
  -webkit-transform: translateX(0%);
      -ms-transform: translateX(0%);
          transform: translateX(0%);
  -webkit-transition: all var(--speed) ease;
  -o-transition: all var(--speed) ease;
  transition: all var(--speed) ease;
}

.menu-primary-exit {
  position: absolute;
}

.menu-primary-exit-active {
  -webkit-transform: translateX(-110%);
      -ms-transform: translateX(-110%);
          transform: translateX(-110%);
  -webkit-transition: all var(--speed) ease;
  -o-transition: all var(--speed) ease;
  transition: all var(--speed) ease;
}

.menu-secondary-enter {
  -webkit-transform: translateX(110%);
      -ms-transform: translateX(110%);
          transform: translateX(110%);
}
.menu-secondary-enter-active {
  -webkit-transform: translateX(0%);
      -ms-transform: translateX(0%);
          transform: translateX(0%);
  -webkit-transition: all var(--speed) ease;
  -o-transition: all var(--speed) ease;
  transition: all var(--speed) ease;
}
.menu-secondary-exit {

}
.menu-secondary-exit-active {
  -webkit-transform: translateX(110%);
      -ms-transform: translateX(110%);
          transform: translateX(110%);
  -webkit-transition: all var(--speed) ease;
  -o-transition: all var(--speed) ease;
  transition: all var(--speed) ease;
}

.dropdown {
  position: absolute;
  top: 58px;
  width: 300px;
  -webkit-transform: translateX(-45%);
      -ms-transform: translateX(-45%);
          transform: translateX(-45%);
  background-color: var(--bg);
  border: var(--border);
  border-radius: var(--border-radius);
  padding: 1rem;
  overflow: hidden;
  -webkit-transition: height var(--speed) ease;
  -o-transition: height var(--speed) ease;
  transition: height var(--speed) ease;
}
Enter fullscreen mode Exit fullscreen mode

Full App.js code below:

import { ReactComponent as BellIcon } from './icons/bell.svg';
import { ReactComponent as MessengerIcon } from './icons/messenger.svg';
import { ReactComponent as CaretIcon } from './icons/caret.svg';
import { ReactComponent as PlusIcon } from './icons/plus.svg';
import { ReactComponent as CogIcon } from './icons/cog.svg';
import { ReactComponent as ChevronIcon } from './icons/chevron.svg';
import { ReactComponent as ArrowIcon } from './icons/arrow.svg';
import { ReactComponent as BoltIcon } from './icons/bolt.svg';


import React, { useState, useRef, useEffect } from "react";
import { CSSTransition } from "react-transition-group";

function App() {
  return (
    <Navbar>
      <NavItem icon={ <PlusIcon /> } />
      <NavItem icon={ <BellIcon /> } />
      <NavItem icon={ <MessengerIcon /> } />

      <NavItem icon={<CaretIcon />}>
        <DropdownMenu></DropdownMenu>
      </NavItem>
    </Navbar>
  ); 
}

function DropdownMenu() {

  const [activeMenu, setActiveMenu] = useState("main");
  const [menuHeight, setMenuHeight] = useState(null);

  function calcHeight(el) {
    const height = el.offsetHeight;
    setMenuHeight(height);
  }

  const mainMenuRef = useRef(null);
  const settingsMenuRef = useRef(null);

  function DropdownItem(props) {
    return (
      <a href="#" className="menu-item" onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}>
        <span className="icon-button">{props.leftIcon}</span>

        {props.children}

        <span className="icon-right">{props.rightIcon}</span>
      </a>
    );
  }

  return (
    <div className="dropdown" style={{ height: menuHeight }} >
      <CSSTransition 
        in={activeMenu === "main"}
        unmountOnExit
        timeout={500}
        classNames="menu-primary"
        nodeRef={mainMenuRef}
        onEnter={calcHeight}
        >
          <div className="menu" ref={mainMenuRef}>
            <DropdownItem>My Profile</DropdownItem>
            <DropdownItem
              leftIcon={<CogIcon />}
              rightIcon={<ChevronIcon />}
              goToMenu="settings"
              >
              Settings
            </DropdownItem>
          </div>
      </CSSTransition>  

      <CSSTransition 
        in={activeMenu === "settings"}
        unmountOnExit
        timeout={500}
        classNames="menu-secondary"
        nodeRef={settingsMenuRef}
        >
          <div className="menu" ref={settingsMenuRef}>
            <DropdownItem leftIcon={<ArrowIcon />} goToMenu="main" />
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
            <DropdownItem>Settings</DropdownItem>
          </div>
      </CSSTransition> 
    </div>
  )
}

function Navbar(props) {
  return (
    <nav className="navbar">
      <ul className="navbar-nav">{ props.children }</ul>
    </nav>
  )
}

function NavItem(props) {

  const [open, setOpen] = useState(false);

  return (
    <li className="nav-item">
      <a href="#" className="icon-button" onClick={() => setOpen(!open)}>
        { props.icon }
      </a>

      {open && props.children}
    </li>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)