WolverStone
WolverStone

Reputation: 11

NextJS Unhandled Runtime Error Error: Hydration failed because the initial UI does not match what was rendered on the server

I'm trying to make a navbar in nextjs using this tutorial https://blog.logrocket.com/how-create-multilevel-dropdown-menu-react/

The navbar works but the app displays this error

Unhandled Runtime Error

Error: Hydration failed because the initial UI does not match what was rendered on the server

How to fix that? It's the first time I've tried something like this, so I don't understand it.

import { useState, useEffect, useRef } from 'react';
import Dropdown from './Dropdown';

import Link from 'next/link';

const MenuItems = ({ items, depthLevel }) => {
  const [dropdown, setDropdown] = useState(false);

  let ref = useRef();

  useEffect(() => {
    const handler = (event) => {
      if (
        dropdown &&
        ref.current &&
        !ref.current.contains(event.target)
      ) {
        setDropdown(false);
      }
    };
    document.addEventListener('mousedown', handler);
    document.addEventListener('touchstart', handler);
    return () => {
      // Cleanup the event listener
      document.removeEventListener('mousedown', handler);
      document.removeEventListener('touchstart', handler);
    };
  }, [dropdown]);

  const onMouseEnter = () => {
    window.innerWidth > 960 && setDropdown(true);
  };

  const onMouseLeave = () => {
    window.innerWidth > 960 && setDropdown(false);
  };

  const closeDropdown = () => {
    dropdown && setDropdown(false);
  };

  return (
    <li
      className="menu-items"
      ref={ref}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      onClick={closeDropdown}
    >
      {items.url && items.submenu ? (
        <>
        {(typeof window !== 'undefined') &&
          <button
            type="button"
            aria-haspopup="menu"
            aria-expanded={dropdown ? 'true' : 'false'}
            onClick={() => setDropdown((prev) => !prev)}
          >
            {window.innerWidth < 960 && depthLevel === 0 ? (
              items.title
            ) : (
              <Link href={items.url}>{items.title}</Link>
            )}

            {depthLevel > 0 &&
            window.innerWidth < 960 ? null : depthLevel > 0 &&
              window.innerWidth > 960 ? (
              <span>&raquo;</span>
            ) : (
              <span className="arrow" />
            )}
          </button>
}
          <Dropdown
            depthLevel={depthLevel}
            submenus={items.submenu}
            dropdown={dropdown}
          />
        </>
      ) : !items.url && items.submenu ? (
        <>
          <button
            type="button"
            aria-haspopup="menu"
            aria-expanded={dropdown ? 'true' : 'false'}
            onClick={() => setDropdown((prev) => !prev)}
          >
            {items.title}{' '}
            {depthLevel > 0 ? (
              <span>&raquo;</span>
            ) : (
              <span className="arrow" />
            )}
          </button>
          <Dropdown
            depthLevel={depthLevel}
            submenus={items.submenu}
            dropdown={dropdown}
          />
        </>
      ) : (
        <Link href={items.url}>{items.title}</Link>
      )}
    </li>
  );
};

export default MenuItems;

Upvotes: 1

Views: 1222

Answers (1)

blorg
blorg

Reputation: 11

I had the same issue when rendering a responsive navigation bar. In my case I have this conditional rendering of the navigation bar based on the user's screen width.

import Link from 'next/link';
import { useMediaQuery } from 'react-responsive';
import { Menu, Dropdown, DropdownMenu } from 'semantic-ui-react';
// ...

function NavMenu(props: NavMenuProps) {
    // ...
    const isMobile = useMediaQuery({ query: '(max-width: 991px)' });

    return (
        <Menu as="nav">

            { !isMobile &&
              <Menu.Menu>
                  <Menu.Item>
                      <Link href="/">
                          Home
                      </Link>
                  </Menu.Item>
              {/* ... */}
              </Menu.Menu>
            }
            { isMobile &&
              <Dropdown
                  item
                  icon='bars'
              >
                  <Dropdown.Menu>
                      <Dropdown.Item>
                          <Link href="/">
                              Home
                          </Link>
                      </Dropdown.Item>
                  {/* ... */
                  </Dropdown.Menu>
              </Dropdown>
            }
        </Menu>
    );
}

What I think causes the issue, is that the system confuses these two (Menu.Menu and Dropdown), because they are rendered in the same place.

So the solution was to add a key attribute to both.

<Menu.Menu key="desktop">
<Dropdown
  item
  icon='bars'
  key='mobile'
>

Upvotes: 1

Related Questions