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
}