dansan
dansan

Reputation: 249

Proper way to toggle class on body with React component?

What's the proper React way of toggling a class on a tag outside of the specific component?

So currently I have the following working example:

import React, { useState, useEffect } from "react";

export default function Header() {
  const [toggled, setToggled] = useState(0);

  useEffect(() => {
    document.body.classList.toggle("toggled");
  });

  return (
    <nav>
      <ul>
        <li>
          <a onClick={() => setToggled(!toggled)} href="#">
            Open menu
          </a>
        </li>
      </ul>
    </nav>
  );
}

This feels wrong for some reason.

First of all I'm using document.body.classList.toggle instead of the useState variable. I know when toggling a class inside this Header component I would just use useState instead of useEffect. But when I need to toggle some class outside the component, like in my case, is that possible with useState alone?

Second of all, should I even be using document.body.classList.toggle in the useEffect, or is there some more specific way of doing it? What if I wanted to target some other element without any id or class?

Lastly, if anyone is wondering, I'm toggling the class on the body instead of directly inside the header component because I want to modify many different elements down the line, not just the menu.

Upvotes: 0

Views: 975

Answers (2)

user3899829
user3899829

Reputation: 1

I came across this problem/solution and wanted to suggest for future-me that changing using body:has(main.show-nav) might be the easiest. Yes, you're adding some to the global CSS but adding/removing that .nav-show at the component level is way easier than trying to pass state up and down your app.

Share and enjoy.

Upvotes: 0

moonwave99
moonwave99

Reputation: 22810

Looks like an X/Y problem - if you want an app-wide UI change, you should change the status on the main component (that holds the parent HTMLElement of the whole app ideally), like:

function App() {
    const [showNav, setShowNav] = useState(false);

    function toggleNav() {
        setShowNav(!showNav);
    }

    return (
        <main className={showNav ? 'show-nav' : ''}>
            <Header onButtonClick={toggleNav}/>
            ...
        </main>
    );
}

function Header({ onButtonClick }) {
    return (
        <header>
            <button onClick={onButtonClick}>Menu</button>
            ...
        </header>
    );
}

If you need to pass the function various levels of hierarchy down the component tree, I recommend to use a state container like redux.

Upvotes: 1

Related Questions