Home > database >  ReactJS hamburger menu is not working after click
ReactJS hamburger menu is not working after click

Time:11-03

I have a problem with my responsive navbar. When I click the hamburger menu it redirects me to a blank white page without anything else. What is the problem? I am new to react and I know i should take another approach to this probably. I tried to find on youtube some tutorials but all of them are made using styled-components. For now i preffer to stick with css.

This is my react component

import React, {useState} from 'react'
import './navbar.css'
import ProfileIcon from '../../assets/profileIcon.svg'

function Navbar() {

    const [showMenu, setShowMenu] = useState(false)

    const navMenu = document.querySelector('.nav-menu')

    if(showMenu) {
        return navMenu;
    }

    return (
        <nav className="navbar">
            {/* logo */}
            <a href="#" className="nav-logo nav-a">Logo</a>

            {/* navbar buttons */}
            <ul className="nav-menu">
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Components</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Articles</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">About</a>
                </li>
            </ul>

            <img src={ ProfileIcon } className="profile-icon" alt="profile-icon"></img>

            {/* hamburger menu */}
            <div className="hamburger" onClick={() => setShowMenu(!showMenu)}>
                <span className="bar"></span>
                <span className="bar"></span>
                <span className="bar"></span>
            </div>

        </nav>
    )
}

export default Navbar

and this is the css file

.navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 10vh;
    background-color: lightcyan;
    padding-left: 20px;
    padding-right: 20px;
}

.nav-li {
    list-style: none;
    cursor: pointer;
    margin: 5px 10px;
    padding: 5px 10px;
    
}

.nav-a {
    text-decoration: none;
    color: black;
}

.nav-link:hover {
    color: red;
}

.profile-icon {
    width: 20px;
    height: 20px;
    cursor: pointer;
}

.bar {
    display: block;
    width: 25px;
    height: 3px;
    margin: 5px auto;
    -webkit-transition: all 0.3s ease-in-out;
    transition: all 0.3s ease-in-out;
    background-color: #101010;
}

.hamburger {
    display: none;
}

.nav-menu {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 30rem;
}

.nav-logo {
    font-weight: 700;
    font-size: 2.1rem;
}

.nav-link{
    font-size: 1rem;
    font-weight: 400;
    color: black;
}

@media only screen and (max-width: 768px) {
    .nav-menu {
        position: fixed;
        left: -100%;
        top: 4rem;
        flex-direction: column;
        background-color: lightgreen;
        width: 100%;
        text-align: center;
        transition: 0.3s;
        box-shadow: 0 10px 27px rgba(0, 0, 0, 0.05);
        
    }

    .nav-menu.active {
        left: 0;
    }

    .nav-item {
        margin: 2.5rem 0;
    }

    .hamburger {
        display: block;
        cursor: pointer;
    }

    .hamburger.active .bar:nth-child(2) {
        opacity: 0;
    }

    .hamburger.active .bar:nth-child(1) {
        transform: translateY(8px) rotate(45deg);
    }

    .hamburger.active .bar:nth-child(3) {
        transform: translateY(-8px) rotate(-45deg);
    }

    
}

CodePudding user response:

React elements (<nav>) are not DOM Nodes. They are plain js data structure. You can't return real DOM collections from react component.

Instead you need to return value that is correct UI representation of your state. For example like below.

Also if the next state depends on the previous state you should use callback form of setState i.e. setShowMenu(showMenu => !showMenu)

import React, {useState} from 'react'
import './navbar.css'
import ProfileIcon from '../../assets/profileIcon.svg'

function Navbar() {

    const [showMenu, setShowMenu] = useState(false)

    return (
        <nav className="navbar">
            {/* logo */}
            <a href="#" className="nav-logo nav-a">Logo</a>

            {/* navbar buttons */}
            {showMenu && (
            <ul className="nav-menu">
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Components</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Articles</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">About</a>
                </li>
            </ul>)}

            <img src={ ProfileIcon } className="profile-icon" alt="profile-icon"></img>

            {/* hamburger menu */}
            <div className="hamburger" onClick={() => setShowMenu(showMenu => !showMenu)}>
                <span className="bar"></span>
                <span className="bar"></span>
                <span className="bar"></span>
            </div>

        </nav>
    )
}

export default Navbar
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

when someone clicks on hamburger menu, you set showMenu to true, so the component rerenders (because the showMenu is a state) firstly it sees your if statement; The condition is true, so goes inside and see a return statement, so it returns a navMenu, but you didn't define a navMenu, and navMenu cannot be a component (it starts with a lowercase letter, components must start with uppercase letter) so it renders your navMenu only and your navMenu is nothing! so you see a blank page.

you need to render the menu conditionally inside your return not separately.

import React, {useState} from 'react'
import './navbar.css'
import ProfileIcon from '../../assets/profileIcon.svg'


function Navbar() {

    const [showMenu, setShowMenu] = useState(false)

   

   

    return (
        <nav className="navbar">
            {/* logo */}
            <a href="#" className="nav-logo nav-a">Logo</a>

            {/* navbar buttons */}
           {showMenu && (<ul className="nav-menu">
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Components</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">Articles</a>
                </li>
                <li className="nav-item nav-li">
                    <a href="#" className="nav-link nav-a">About</a>
                </li>
            </ul>)}

            <img src={ ProfileIcon } className="profile-icon" alt="profile-icon"></img>

            {/* hamburger menu */}
            <div className="hamburger" onClick={() => setShowMenu(!showMenu)}>
                <span className="bar"></span>
                <span className="bar"></span>
                <span className="bar"></span>
            </div>
        </nav>
    )
}

export default Navbar

CodePudding user response:

In React, what the component renders is a declarative function of props and state.

There is generally no need to use document.querySelector to do anything with the DOM.

Instead, use the value of the showMenu state to conditionally hide, show or change attributes of various elements.

In the below example I'm adding and removing the "active" className).

import "./styles.css";

import React, { useState } from "react";

function Navbar() {
  const [showMenu, setShowMenu] = useState(false);

  return (
    <nav className={`navbar`}>
      {/* navbar buttons */}
      <ul className={`nav-menu ${showMenu && "active"}`}></ul>

      {/* hamburger menu */}
      <button className="hamburger" onClick={() => setShowMenu(!showMenu)}>
        hamburger
      </button>
    </nav>
  );
}

export default Navbar;

Edit beautiful-lamarr-73r8u

  • Related