Home > Back-end >  React - Updating State From a Child Component, Then Doing Something Based on State
React - Updating State From a Child Component, Then Doing Something Based on State

Time:05-09

New to React hooks and state, trying to figure out why my code isn't working.

I have an App.js file and a child component called Menu.js. Essentially, Menu.js contains a menu that I want to show if a variable called isOpen is true, and hide if isOpen is false. I have added the function at the parent level on App.js, as I have another component that needs to be able to update the state of isOpen as well, but that component is not relevant to my question. Here is the relevant code:

App.js:

import Menu from "./components/Menu";
import { useState } from "react";

export default function App() {
  const [isOpen, setIsOpen] = useState(false);
  const toggleState = () => setIsOpen(!isOpen);
  console.log(isOpen); 
    // This will console log either true or false

  const menuBtn = document.querySelector(".hamburgermenu");
  const mobileMenu = document.querySelector(".mobilemenu");
    // These are elements on the child component, Menu.js

  if (isOpen) {
    menuBtn.classList.add("open");
    mobileMenu.style.zIndex = "5";
      // This should reveal the menu if the value of isOpen is true
  } else {
      menuBtn.classList.remove("open");
      mobileMenu.style.zIndex = "0";
        // This should hide the menu if the value of isOpen is false
  }

return (
  <div>
    <Menu togglestate={toggleState} /> 
      // Passing togglestate down to Menu.js as a prop
  </div>
  );
}

Menu.js:

export default function Menu(props) {
  return (
    <div>
      <div className="hamburgermenu" onClick={props.togglestate}>Stuff in here</div>
      // Clicking this div will toggle the value of isOpen between true and false
      <div className="mobilemenu">Stuff in here</div>
    </div>
  );
}

When I try to run that, my whole application breaks and I get the error TypeError: Cannot read properties of null (reading 'classList') at menuBtn.classList.remove("open");

I know that my problem lies in that the default state is false, so it will first try to run the else statement. However, because the class open hasn't been added to the menuBtn element yet, there is no open class to remove, and it's erroring out.

I tried to add another nested if/else statement within the original else statement, basically saying that if the element has the class called open, remove it, like this:

  if (isOpen) {
    menuBtn.classList.add("open");
    mobileMenu.style.zIndex = "5";
  } else {
      if (menuBtn.classList.contains("open")) {
        menuBtn.classList.remove("open");
        mobileMenu.style.zIndex = "0";
      } else {
          mobileMenu.style.zIndex = "0";
      }
  }

I also tried moving away from classes entirely and just changing the styles directly as a test, like this:

if (menuOpen) {
  menuBtn.style.display = "block";
  mobileMenu.style.zIndex = "5";
} else {
  menuBtn.style.display = "none";
  mobileMenu.style.zIndex = "0";
}

But I still got an error at the same line, TypeError: Cannot read properties of null (reading 'style') at menuBtn.style.display = "block";

I know this is all a little messy. I'm sure there's a better way of doing this, but this is how I would go about it with the knowledge that I do have. If there is an easier way, I am open to suggestions!

CodePudding user response:

General idea in React is to not have to deal with the DOM directly with stuff like document.querySelector(".hamburgermenu")

Instead of creating a new function toggleState, you can directly pass the setter function setIsOpen into the Menu function as follows (along with the current open state)

<Menu isOpen={isOpen} togglestate={setIsOpen} />

Then your whole if-else condition can be simplified down to a ternary operator as follows:

export default function Menu({ isOpen, togglestate }) {
    return (
        <>
            <div
                className={'hamburgermenu'   isOpen ? ' open' : ''}
                onClick={() => togglestate(!isOpen)}
            >
                // Clicking this div will toggle the value of isOpen between true and
                false
            </div>
            <div className="mobilemenu" style={{ zIndex: isOpen ? 5 : 1 }}>
                Stuff in here
            </div>
        </>
    );
}
  • Related