Home > Back-end >  onClick not working in React functional component
onClick not working in React functional component

Time:10-02

function Navbar() {

    const [isDark, setIsDark] = useState(false);

   useEffect(() => {
    if (isDark) {
        let body = document.getElementsByTagName("body")[0];
        body.classList.add("dark");
        let darkIcon = document.getElementsByClassName("dark-icon")[0];
        darkIcon.style.display = "none";
        
    } else {
        let lightIcon = document.getElementsByClassName("light-icon")[0];
        lightIcon.style.display = "none";
        
    }
   }, [isDark])

    

    return (
        <div class="navbar">
        
        <h1>devfinder</h1>
        <div className="toggle">
        <p onClick={() => {
            setIsDark(!isDark);
        }}className="light-icon">Light <img src={sunIcon} alt="Sun"/> </p>
        <p onClick={() => {
            setIsDark(!isDark);
        }} className="dark-icon">Dark  <img src={moonIcon} alt="Moon"/></p>
        
        </div>
        </div>
    )
}

export default Navbar;

The onClick functions that I have in my paragraph tags aren't working and I have no clue why. There are no errors in the console, the function is just never called. Any suggestions?

CodePudding user response:

In React, it is incorrect to use DOM methods. React handles everything for you. Here is your code refactored a bit to likely do what you want it to:

import moonIcon from 'some/directory'
import sunIcon from 'some/directory'

function Navbar() {

    const [isDark, setIsDark] = useState(false);
    const [text, setText] = useState("Light")
    const [iconClass, setIconClass] = useState("light-icon")
    const [icon, setIcon] = useState(sunIcon)
    const darkIcon = ( <img src={moonIcon} alt="Moon"/> )
    const lightIcon = ( <img src={sunIcon} alt="Sun"/> )

   useEffect(() => {
    if (isDark) {
        setIcon(moonIcon)
        setText("Dark")
        setIconClass("dark-icon")
    } else {
      setIcon(sunIcon)
      setText("Light")
      setIconClass("light-icon")
    }
   }, [isDark])

    

    return (
        <div class="navbar">
          <h1>devfinder</h1>
          <div className="toggle">
            <p className={iconClass} onClick={() => { setIsDark(!isDark)}}>
                 {text}{icon}
            </p>       
        </div>
        </div>
    )
}

export default Navbar;

Breaking this code down is important because it changes a lot about your approach and makes it more of a "React" approach by using the useState hook to store the state of several different elements.

const [text, setText] = useState("Light")
const [iconClass, setIconClass] = useState("light-icon")
const [icon, setIcon] = useState(sunIcon)

We can store the state of text, class names, and even which icon to display. useState is very powerful!

const darkIcon = ( <img src={moonIcon} alt="Moon"/> )
const lightIcon = ( <img src={sunIcon} alt="Sun"/> )

You can also store html code as a constant using jsx, you aren't limited to just returning it. This means we can pass html code around as a variable!

useEffect(() => {
    if (isDark) {
        setIcon(moonIcon)
        setText("Dark")
        setIconClass("dark-icon")
    } else {
      setIcon(sunIcon)
      setText("Light")
      setIconClass("light-icon")
    }
   }, [isDark])

The useEffect section just replaces your DOM methods with state updates. How does it work?

<p className={iconClass} onClick={() => { setIsDark(!isDark)}}>
  {text}{icon}
</p>

Notice how in your returned html, I've reduced your paragraphs down from two just to one. That's because instead of having two paragraphs that conditionally set their display css, you can actually just have one paragraph that conditionally displays html elements and images!

useEffect in React will run code within it once per frame. So what it's doing is checking if the state isDark has changed (via onClick). If it has, then perform some update. The updates: set the icon (to either moon or sun), set the text (the text next to the icon, either "Light" or "Dark"), and set the class of the paragraph (because I noticed that class changes based on dark vs light mode).

Referring back to the paragraph: you can pass your JavaScript variables into your html if you wrap them with curly braces (className={iconClass} will set the className to whatever iconClass's value is, etc.).

When you click the paragraph, the variables update, and the html will also update to have the new values.

CodePudding user response:

I guess you need to switch between light and dark mode here is my simple illustration for that to get a basic idea for you

export default function App() {
  const [dark, setDark] = useState(false);

  return (
    <div className="App">
      <div class="navbar">
        <h1>devfinder</h1>
        <h4>color mode is {!dark ? "light" : "dark"}</h4>
        <div className="toggle">
          <div onClick={() => setDark(false)} className="light-icon">
            Light
            <i class="fas fa-sun"></i>
          </div>
          <div onClick={() => setDark(true)} className="dark-icon">
            Dark
            <i class="fas fa-moon"></i>
          </div>
        </div>
      </div>
    </div>
  );
}

this switch state and you can do whatever you want with state working example - https://codesandbox.io/s/silly-framework-rsgue

CodePudding user response:

If you simply want to toggle between dark and light remove the unnecessary useEffect (don't use native DOM methods with React), and use a ternary operator in your return to determine which paragraph/icon should be used when the component re-renders when the state changes.

Note: I've used font-awesome here because I don't have access to your images.

const { useState } = React;

function Example() {

  const [isDark, setIsDark] = useState(false);

  return (
    <div class="navbar">
      <h1>devfinder</h1>
      <div className="toggle">
        {isDark ? (
          <p
            onClick={() => setIsDark(!isDark)}
            className="dark"
          >Dark&nbsp;
            <i className="fa-solid fa-moon"></i>
          </p>
        ) : (
          <p
            onClick={() => setIsDark(!isDark)}
            className="light"
          >Light&nbsp;
            <i class="fa-solid fa-sun"></i>
          </p>
        )}
      </div>
    </div>
  );
}

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
p { padding: 5px 0 5px 5px; } 
p:hover { cursor: pointer; }
.light { background-color: #FAFAD2; color: black; }
.dark { background-color: #333333; color: white; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  • Related