0

I have a darkMode toggle as a separate component (grandchild) which is embedded in a Navbar component (child). That Navbar component is called in the App component (parent).

Essentially, my goal is that when I toggle the darkMode/lightMode button in the DarkMode component, it sends this 'event' through the Navbar and up to the App.js where the CSS change will be called and is dependent on a conditional operator.

I've tried to pass some props with what basic knowledge I have, but I can't seem to work out how to actually pass the state/event.

I'm not sure if what I am trying to do is even achievable and/or is too complex, so would really appreciate suggestions. I know I can place all of this code in the App.js, but I wanted to separate my components.

App.js (parent)

const App = () => {
  const [darkMode, setDarkMode] = useState(true);

  return (
    <div className={darkMode ? "darkmode" : "lightmode"}>
      <Navbar darkMode={darkMode}/>
      <Hero />
    </div>
  );
}

Navbar.js (child)

const Navbar = ({darkMode}) => {
  return (
    <div className="row">
      <div className="col">
        <h3>This is the Navbar component</h3>
      </div>
      <div className="col">
        {/* This is the DarkMode component button */}
        <DarkMode darkMode={darkMode} />
      </div>
    </div>
  )
}      

DarkMode.js (grandchild)

const DarkMode = ({darkMode}) => {

  const [isDarkMode, setDarkMode] = useState(false);
  const [imageSrc, setImageSrc] = useState(SunIcon);

  const switchModes = () => {
    setDarkMode(prevMode => !prevMode);
    isDarkMode ? setImageSrc(SunIcon) : setImageSrc(MoonIcon);
  };

  return (
    <>
      <button>
        <img
          className=""
          onClick={switchModes}
          src={imageSrc}
          alt="lightning-bolt"
          height="30px"
        />
      </button>
    </>
  )
}
BuiltByDan
  • 81
  • 2
  • 6

1 Answers1

1

THere two main ways of solving your issue:

  1. First it be using React Context to control & store the value of the current theme(dark/light).
  2. As you have mostly done, would be using prop drilling to set the state(global in the App.js). I think this is easier & simpler in this particular case.

The issue with your code is that you're successfully passing down the value of the current darkMode, i.e., whether it's enabled or not, but not the state setter to update it in the child and have it driven by the parent/global state(the one in App.js).

You should update your code as follows and it should work:

App.js

const App = () => {
  const [darkMode, setDarkMode] = useState(true);

  return (
    <div className={darkMode ? "darkmode" : "lightmode"}>
      <Navbar darkMode={darkMode} setDarkMode={setDarkMode}/>
      <Hero />
    </div>
  );
}

Navbar.js(child)

const Navbar = ({darkMode, setDarkMode}) => {
  return (
    <div className="row">
      <div className="col">
        <h3>This is the Navbar component</h3>
      </div>
      <div className="col">
        {/* This is the DarkMode component button */}
        <DarkMode darkMode={darkMode} setDarkMode={setDarkMode} />
      </div>
    </div>
  )
} 

DarkMode.js(grandchild)

const DarkMode = ({darkMode, setDarkMode}) => {
  const [imageSrc, setImageSrc] = useState(SunIcon);

  const switchModes = () => {
    setDarkMode(!darkMode);
  };

  return (
    <>
      <button>
        <img
          className=""
          onClick={switchModes}
          src={darkMode ? SunIcon : MoonIcon}
          alt="lightning-bolt"
          height="30px"
        />
      </button>
    </>
  )
}
Abdulrahman Ali
  • 603
  • 1
  • 4
  • 15
  • This worked perfectly. May I just ask in the DarkMode.js file, for the line: `src={darkMode ? SunIcon : MoonIcon}` - how is the image response changing to this as I can't see the state in that file? Is the App.js sending that information back to the component? – BuiltByDan May 16 '23 at 20:20
  • 1
    @BuiltByDan Yes, this way you depend on the global state defined in the uppermost parent `App.js` and have the child components drive their info via `props` only so that whenever the global parent state changes, their props will automatically change. Now, here: `src={darkMode ? SunIcon : MoonIcon}` - This means that whenever the state of the `App.js` file changes, the children components will re-render with a new value of `darkMode` thus toggling between the values of the two images. Hope this helps. – Abdulrahman Ali May 17 '23 at 12:04
  • 1
    @BuiltByDan Another thing to add, is that this line: `const [imageSrc, setImageSrc] = useState(SunIcon);` is not necessary here and can be removed since we are driving the child component props from its global parent state, also known as `controlled components`. Read more [here](https://stackoverflow.com/questions/42522515/what-are-react-controlled-components-and-uncontrolled-components) – Abdulrahman Ali May 17 '23 at 12:14