D1ma6
D1ma6

Reputation: 31

How to change class on hover in React without useState or so that not all elements are triggered

I want to apply hover effects in React when the element (span) is hovered however when i do that to multiple elements and hover one of them, all of them are triggered. I want to do multiple of these span elements however when I hover one all of them are changing.

import React, { useState } from "react";
import { Link } from "react-router-dom";
import { Animated } from "react-animated-css";

// styles
import styles from "./Intro.module.css";

function Intro() {
  const [hovered, setHovered] = useState(false);
  const toggleHover = () => setHovered(!hovered);
  const toggleHover2 = () =>
    setTimeout(() => {
      setHovered(!hovered);
    }, 700);

  return (
    <section className={styles.container}>
      <div className={styles.inner}>
        <span className={styles.hello}>hello,</span>
        <h1 className={styles.title}>
          My name is

          <!-- This bit -->

          <span
            className={hovered ? "animated rubberBand" : ""}
            onMouseEnter={toggleHover}
            onMouseLeave={toggleHover2}
          >
            D
          </span>

        <!-- This bit -->
          mytro Bula
        </h1>
        <h1 className={styles.title}>
          I turn <span className={styles.titleCoffee}>coffee</span> into code
        </h1>
        <p className={styles.para}>
          I'm a <span className={styles.paraRed}>front-end web developer</span>
          <span className={styles.paraBlue}>/designer</span> from Ukraine based
          in London, I love creating beatiful and functional{" "}
          <span className={styles.paraRed}>websites</span>,{" "}
          <span className={styles.paraRed}>applications</span>, and everything
          in between.
        </p>
        <Link to="/Contact" className={styles.btn}>
          Get in touch
        </Link>
      </div>
    </section>
  );
}

export default Intro;

Upvotes: 3

Views: 2607

Answers (4)

Luke Storry
Luke Storry

Reputation: 6702

I'd recommend using CSS's :hover pseudo-class to change styles for elements upon hover - it's more performant and less likely to cause bugs like the one you're seeing.

But if you want to keep doing it in React, you should split out that span into a new component, and move all the hover-related state into that component. At the moment you have one "hovered" state being shared by all the elements, which is why you're seeing them all change when one of them is hovered over. By putting the setState in the separate component, and reusing that component within this Intro component, each one will have its own version of hover. :)

Upvotes: 2

Samuel Hulla
Samuel Hulla

Reputation: 7089

Well I mean the answer as to why it is happening is pretty obvious.

You are passing the state hovered to every single className, so hence once you call setHovered(true) this will cause your entire component to re-render and change the className for every single span as per this line:

className={hovered ? "animated rubberBand" : ""}

If you really insist on using for this (hint, you shouldn't), you will need to add specifity to your state, eg.

const hoverObject = {
  name: false,
  age: false,
}

const [hover, setHover] = useState(hoverObject)

const toggleHover = (event) => {
  const { name } = event.target
  setHover(prevHover => ({
    ...prevHover,
    [name]: !prevHover[name]
  })
}

// I'm leaving out onMouseLeave etc here for sake of example simplicity

return (
  <React.Fragment>
    <input
      type="text"
      name="name"
      className={hovered.name ? "animated rubberBand" : ""}
      onMouseEnter={handleHover}
    />
    <input
      type="text
      name="age"
      className={hovered.age ? "animated rubberBand" : ""}
      onMouseEnter={handleHover}
    />
  </React.Fragment>
)

That being said, this is a total overkill, and you should really just be using the CSS :hover pseudoselector

// JSX 
<input type="text" className="whatever hoverAble" />
// CSS
.hoverAble:hover {
  // do something
}

Upvotes: 0

Othman
Othman

Reputation: 72

Just my two cents but have you try, hover with css ---> https://www.w3schools.com/cssref/sel_hover.asp. It would do what you need without creating different states but you might need to add classes to your spans for them to be hovered on their own. For example you would write this in your css: .span1:hover{your class changes for the 1st span} .span2:hover{your class changes for the 2nd one}

If not you will have to create different states for different elements. When a state is used for few elements all of them will follow it, that is why you would need a state for each element that you want to be separated from the other elements (span in your case).

Is that helping you ?

Upvotes: 0

Kyle Mills
Kyle Mills

Reputation: 373

So before reaching for something in React I like to think, how would I do this in HTML/CSS or plain Javascript. You can think of the HTML as a tree structure.

   ROOT
   /  \
CHILD CHILD

So you would simply need to apply a new class to the top most element relative to all your children you would like the effect applied.

HTML

<div class="add_effect">
  <!-- MY Child Elements that need the effect here -->
</div> 
  

CSS

.add_effect .my_element {
 // Styles here
}

Now to do this in React, you would simply hoist the state up.

With your state hoisted up, when a hover is triggered, simply hook into this state to add the css class at the top most level. CSS will take care of applying the styles to all the child elements.

Upvotes: 0

Related Questions