Home > Mobile >  Changing image source on mouseover/mouseout in React/Next.js
Changing image source on mouseover/mouseout in React/Next.js

Time:07-19

Technologies I use:

Next.js Styled-Components Typescript

What I want to do:

I wish to change image to a different one when I hover over the parent div that wraps it (Card).

What is my solution:

I managed to do that with use of useState like so:

const [isHovering, setIsHovered] = useState(false);
const [isHovering2, setIsHovered2] = useState(false);
const [isHovering3, setIsHovered3] = useState(false);


  

      <Card1>
        <Card
          onm ouseEnter={() => setIsHovered(true)}
          onm ouseLeave={() => setIsHovered(false)}
        >
          <IconWrapper>
            {isHovering ? (
              <Image layout="fill" src={image1OnHover} />
            ) : (
              <Image layout="fill" src={image1} />
            )}
          </IconWrapper>
          <CardText>
            <h4>Title</h4>
            <p>
              Dorem Ipsum.
            </p>
          </CardText>
        </Card>
      </Card1>

      <Card2>
        <Card
          onm ouseEnter={() => setIsHovered2(true)}
          onm ouseLeave={() => setIsHovered2(false)}
        >
          <IconWrapper>
            {isHovering2 ? (
              <Image layout="fill" src={image2OnHover} />
            ) : (
              <Image layout="fill" src={image2} />
            )}
          </IconWrapper>
          <CardText>
            <h4>Title</h4>
            <p>
              Dorem Ipsum
            </p>
          </CardText>
        </Card>
      </Card2>

      <Card3>
        <Card
          onm ouseEnter={() => setIsHovered3(true)}
          onm ouseLeave={() => setIsHovered3(false)}
        >
          <IconWrapper>
            {isHovering3 ? (
              <Image layout="fill" src={image3OnHover} />
            ) : (
              <Image layout="fill" src={image3} />
            )}
          </IconWrapper>
          <CardText>
            <h4>Title</h4>
            <p>
              Dorem Ipsum
            </p>
          </CardText>
        </Card>

What I'd like to ask for:

It seems to me like this code looks awful and is not really following the DRY principle.

It works but I don't think that spamming useStates is a good idea (what if there were 100 images to change... that would be pain to write).

I've been searching web for some better ideas, but I only found examples similar to what I've coded and for singular images. The problem here is that if I was to only use one state, then all of the images would change at the same time - which forces me to use individual hooks for each and every element I add to the page.

This is my first ever request on stackoverflow - I apologize if my post is not up to standards - hopefully it'll do ;)

CodePudding user response:

Another Option is to make a component that handles the state itself then pass the two image options into it. Here is an example of a simple component that does that: https://codesandbox.io/s/hoverimage-ldscek?file=/src/OnHoverImage.js:40-449. Then you could map that component and display that component as many times as you like easily.

export const OnHoverImage = ({ hoveredImage, image, alt }) => {
  const [hover, setHover] = useState(false);
  return (
    <div
      style={{ width: "200px", height: "200px" }}
      onm ouseEnter={() => setHover(true)}
      onm ouseLeave={() => setHover(false)}
    >
      {hover ? (
        <img src={image} alt={alt} />
      ) : (
        <img src={hoveredImage} alt={alt} />
      )}
    </div>
  );
};

Mapping the image would look like this:

<div>
    {arrayOfImages.map((el) => (
            <OnHoverImage
              alt={el.alt}
              hoveredImage={el.hoverImage}
              image={el.image}
            />
          ))}
</div>

CodePudding user response:

There are of course many ways to do this. One such way is by having a single state that represents all images:

const images = [...];

const Component = (props) => {
  const [imageStates, setImageStates] = useState({});

  const setImageHoveringState = (imageId, state) => {
    setImageStates({
      ...imageStates,
      imageId: state,
    });
  };

  return (
    <div>
      {images.map((image) => {
        // If the images don't have an ID, just use their index in the array
        return (
          <Card
            key={image.id}
            onm ouseEnter={() => setImageHoveringState(image.id, true)}
            onm ouseLeave={() => setImageHoveringState(image.id, false)}
          />
            <IconWrapper>
              {imageStates[image.id] ? (
                <Image layout="fill" src={image2OnHover} />
              ) : (
                <Image layout="fill" src={image2} />
              )}
            </IconWrapper>
          </Card>
        );
      })}
    </div>
  );
};

CodePudding user response:

Either having a single state or different states is good. It depends on your preference. Actually different states is more definitive than the object state.

Object:

const [state, useState] = useState({ ... })

Different state

const [state1, useState] = useState()
const [state1, useState] = useState() 
const [state1, useState] = useState() 

Another example, more clear

Having separate variables

    const num1 = 1
    const num2 = 2
    const num3 = 3

or having one object but different properties.

   const nums = {
    num1: 1,
    num2: 2,
    num3: 3
    }
  • Related