Reputation: 11
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>»</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>»</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
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