aromanarguello
aromanarguello

Reputation: 776

reactjs - Render single icon on hover for list item rendered from array

I have this sort of cards that are rendered from an array of objects.

Parent Component:

[{foo: 'bar', baz: [{ name: string, path: string: '/'}]

state = {isHovering: false}

handleMouseHover = () => {
    const { isHovering } = this.state;
    this.setState({ isHovering: !isHovering });
}

;

I'm passing down handleMouseHover() and isHovering down as props to a child component.

Resulting in something like this:

Child Component

<LinkContainer
  onMouseEnter={handleMouseHover}
  onMouseLeave={handleMouseHover}
  className="linkContainer"
>

 {isHovering && (
   <FontAwesomeIcon
     icon="copy"
     className="copyIcon"
     onClick={copyToClipboard}
   />
 )}

The result is 4 cards that contain 3 links. Each time I hover over a link I want the copy to clipboard icon to show. However, at the moment when I hover over any item it sets isHovering to true thus making all the icons visible. Ideally I just want the the icon for the link I hover over to become visible. Can someone help me to figure out a better solution or a refinement of my already written code.

Much appreciated!

Upvotes: 5

Views: 13381

Answers (3)

Arun Khot
Arun Khot

Reputation: 424

There is simple solution with CSS. Just add below CSS to the elements you want to show/hide on hover. By default put set CSS property

{
  display: none
}

Element would be visible on mouse hover with below CSS.

                '&:hover, &:focus-within': {
                  '.element-class': {
                    display: 'block',
                  },
                },

Upvotes: 0

Tholle
Tholle

Reputation: 112807

You could keep an object in your state instead of a boolean, that has a key indicating if the object with that particular key as index is hovered or not.

Example

class App extends React.Component {
  state = {
    arr: [{ text: "foo" }, { text: "bar" }],
    isHovered: {}
  };

  handleMouseEnter = index => {
    this.setState(prevState => {
      return { isHovered: { ...prevState.isHovered, [index]: true } };
    });
  };

  handleMouseLeave = index => {
    this.setState(prevState => {
      return { isHovered: { ...prevState.isHovered, [index]: false } };
    });
  };

  render() {
    const { arr, isHovered } = this.state;

    return (
      <div>
        {arr.map((el, index) => (
          <Child
            onMouseEnter={() => this.handleMouseEnter(index)}
            onMouseLeave={() => this.handleMouseLeave(index)}
            text={el.text}
            isHovering={isHovered[index]}
            key={index}
          />
        ))}
      </div>
    );
  }
}

function Child({ onMouseEnter, onMouseLeave, text, isHovering }) {
  return (
    <div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
      {text} {isHovering && " (hovering!)"}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

Upvotes: 13

Create a property isHovered on item of an array dynamically and onMouseHover pass the item which you get in .map, now toggle the isHovered property. Should work now.

Upvotes: 0

Related Questions