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
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => 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
onMouseEnter={() => setIsHovered2(true)}
onMouseLeave={() => 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
onMouseEnter={() => setIsHovered3(true)}
onMouseLeave={() => 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 ;)
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" }}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => 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>