skip to Main Content

I am trying to make responsive menu bar where the menu needs to be displayed and hidden. It is not working as expected. So, I did the simple demo version to check if it works, but it does not work as well. Please let me know why it is not working.

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [show, setShow] = useState(true);

  console.log("Show value: ", show);

  const menuToggle = () => {
    setShow(!show);
  };

  return (
    <div className="App">
      <div>
        <button type="button" onClick={menuToggle}>
          Show
        </button>
        <button type="button" onClick={menuToggle}>
          Hide
        </button>
      </div>
      <h1>Hello World</h1>
    </div>
  );
}

And styles.css is as follows:

.App {
  font-family: sans-serif;
  text-align: center;
}
h1 {
  opacity: ${({ show }) => (show ? 1 : 0)}; 
}

However, when I set opacity directly as 1 or 0, then it works. It makes me think that the CSS property is not set on component re-rendering. Please correct me where I am going wrong.

Edit:
In my actual project, I need to use styled component and have it under @media screen. Part of my code is:

<NavMenu>
    {data.map((elementList, index) => (
        <NavItem key={index}>
            <NavLinks to={elementList.to}>{elementList.text}</NavLinks>              
        </NavItem>
    ))}
</NavMenu>

and the component NavMenu is defined as:

export const NavMenu = styled.ul`
  width: 100%;
  display: flex;
  text-align: center;
  list-style: none;
  align-items: center;
  justify-content: center;


  @media screen and (max-width: 960px) {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    height: 100vh;
    position: fixed;
    top: 0;
    right: 0;
    opacity: ${({ show }) => (show ? 1 : 0)};
    visibility: ${({ show }) => (show ? "visible" : "hidden")};
    transform: translateY(${({ show }) => (show ? "0" : "-100vh")});
    transition: transform 5s ease;
    background-color: #071c2f;
  }

Thanks

3

Answers


  1. Try applying the conditional styling directly in your JSX.

    <h1 style={{ opacity: show ? 1 : 0 }}>Hello World</h1>
    
    Login or Signup to reply.
  2. You can render conditionally the whole class of css, I think that’s the best approach and no matter the number of classes you can render them conditionally.

    Example:

    <h1 className={`${show ? "visible" : "hidden"}`}>Hello World</h1>
    
    Login or Signup to reply.
  3. I get confused with what you mean by it is not working as expected, do you want to always show the menu bar in large screen and controls it in smaller screen?. About using media queries in react JS, I personally suggest you to use a CSS framework that supports media queries and handle the state the react way.

    If you insist to do media queries with react JS, this is what I come up with.

    import { useEffect, useState } from "react";
    
    const App = () => {
      //control the visibility of the navbar
      const [navOpen, setNavOpen] = useState(true);
      //default value as the user open the web page
      const [match, setMatch] = useState(
        window.matchMedia("(max-width: 960px)").matches
      );
    
      useEffect(() => {
        //update the current match
        const handler = (e) => setMatch(e.matches);
        //add the listener to watch current view size (media query) and change state as soon as it matches
        window.matchMedia("(max-width: 960px)").addEventListener("change", handler);
        //clean up useEffect
        return () => {
          window
            .matchMedia("(max-width: 960px)")
            .removeEventListener("change", handler);
        };
      });
    
      return (
        <>
          <NavMenu match={match} isOpen={navOpen}>
            <ul>1</ul>
            <ul>2</ul>
            <ul>3</ul>
            <ul>4</ul>
            <ul>5</ul>
          </NavMenu>
          <NavTrigger match={match} setNavOpen={setNavOpen} />
        </>
      );
    };
    
    export default App;
    

    the code above is the parent component. The component watch for the screen changes that happens in user’s web browsers.

    const NavTrigger = ({ match, setNavOpen }) => {
      return (
        <div className="App">
          {/* if the current view size is less than 960px show the button */}
          {/* visibility through dom manipulation */}
          {match && (
            <div>
              <button type="button" onClick={() => setNavOpen(true)}>
                Show
              </button>
              <button type="button" onClick={() => setNavOpen(false)}>
                Hide
              </button>
            </div>
          )}
          {/* implementation with inline style to set opcaity */}
          <h1 style={{ opacity: match ? 1 : 0 }}>
            This &lsquo;Hello World&rsquo; only shows on mobile (960px screen size)
          </h1>
        </div>
      );
    };
    

    as for the trigger, the component controls the state of the navbar visibility on screen below 960px.

    const NavMenu = ({ children, match, isOpen }) => {
      return (
        <nav
          style={{
            border: "1px solid red",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            padding: "0 2rem",
            opacity: !match || isOpen ? 1 : 0,
            visibility: !match || isOpen ? "visible" : "hidden",
            transform: !match || isOpen ? "translateX(0)" : "translateX(-100vh)",
          }}
        >
          {children}
        </nav>
      );
    };
    

    the nav accept two state, one is from mediaqueries and one is current open state. So even if the open state is false, in large screen (above 960px) the navbar will always be visible.

    You can later stylize the navbar component with class or inline style. It should not affect the functionality.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search