Displaying text on only one image at at time in React

Viewed 26

I am working on a portfolio page using React and Tailwind. I am trying to get to where when you hover over each image, it will display the text at the bottom of a single image. Right now when I hover over the image, all text displays on all images. How would I fix this? Also, I know there has to be a way to clean this up, I was trying to get it work first. Thank you for and advice you might have!

const Experience = () => {
  const [isHovering, setIsHovering] = useState(false);

  const handleMouseOver = () => {
    setIsHovering(true);
  }

  const handleMouseOut = () => {
    setIsHovering(false);
  }

  return (
    <div
      name="experience"
      className="w-full h-screen bg-[#0a192f] text-gray-300"
    >
      <div className="max-w-[1000px] mx-auto flex flex-col justify-center w-full h-full">
        <div>
          <p className="text-4xl font-bold inline border-b-4 border-purple-800">
            Experience
          </p>
          <p className="py-4">Technologies I have worked with</p>
        </div>
        <div className="w-full grid grid-cols-2 sm:grid-cols-4 gap-4 text-center py-8">
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={CssLogo}
              alt="Css Logo"
            />
            {isHovering ? <p className="font-bold">CSS</p> : ""}
          </div>
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={HtmlLogo}
              alt="HTML logo"
            />
            {isHovering ? <p className="font-bold">HTML</p> : handleMouseOut}
          </div>
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={JavaScriptLogo}
              alt="Js Logo"
            />
            {isHovering ? (
              <p className="font-bold">JavaScript</p>
            ) : (
              handleMouseOut
            )}
          </div>
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={MongoLogo}
              alt="Mongo Logo"
            />
            {isHovering ? (
              <p className="font-bold">Mongo DB</p>
            ) : (
              ''
            )}
          </div>
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={MysqlLogo}
              alt="Mysql Logo"
            />
            {isHovering ? (
              <p className="font-bold">MySQL</p>
            ) : (
              ''
            )}
          </div>
          <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
            <img
              onMouseOver={handleMouseOver}
              onMouseOut={handleMouseOut}
              className="w-20 mx-auto"
              src={NodeLogo}
              alt="Node Logo"
            />
            {isHovering ? (
              <p className="font-bold">Node JS</p>
            ) : (
              ''
            )}
          </div>
1 Answers

You have lots of text elements, but only one state value:

const [isHovering, setIsHovering] = useState(false);

So either all of them are "hovering" or all of them are "not hovering". Instead of a boolean, you might use some kind of identifier. For example:

const [hoveringID, setHoveringID] = useState();

And update the state to an identifier:

const handleMouseOver = (id) => {
  setHoveringID(id);
}

const handleMouseOut = () => {
  setHoveringID(undefined);
}

And pass some ID to those handlers:

onMouseOver={() => handleMouseOver(1)}

(Use a different ID for each element, of course.) And use that state to determine if that element is "hovering":

{hoveringID === 1 ? <p className="font-bold">CSS</p> : ""}

Taking it a step further, this all becomes much easier if you refactor all of that repeated code. Create data for your component:

const sections = [
  {
    id: 1,
    src: CssLogo,
    alt: "Css Logo",
    hoverContent: "CSS"
  },
  //etc...
]

And just map over that data:

<div className="w-full grid grid-cols-2 sm:grid-cols-4 gap-4 text-center py-8">
{
  sections.map(section => (
    <div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
      <img
        onMouseOver={() => handleMouseOver(section.id)}
        onMouseOut={handleMouseOut}
        className="w-20 mx-auto"
        src={section.src}
        alt={section.alt}
      />
      {hoveringID === section.id ? <p className="font-bold">{section.hoverContent}</p> : ""}
    </div>
  ))
}
</div>
Related