Home > Software engineering >  React hover event activating all elements in map
React hover event activating all elements in map

Time:10-29

I am trying to work with a hover event inline in react as a hook. I am really close, but it's activating ALL items in the .map. I am trying to get it to only fire on the specific <a> element that is being hovered on ..

It's a very basic setup:

const data = [
    {id: 1, text: 'Inbox Item 1 -- Subject -- Short description', date: '1.03.2016'},
    {id: 2, text: 'Inbox Item 2 -- Subject -- Short description', date: '23.01.2017'},
    {id: 3, text: 'Inbox Item 3 -- Subject -- Short description', date: '12.01.2022'}
];

const inboxItemStyle = ({hover}) => ({
    display: "block",
    borderBottom: '1px solid #CCC',
    backgroundColor: hover ? '#EEF' : '#FFF',
    padding:"15px",
})

function Inbox() {
    const [hover, setHover] = useState(false);
    return (
      <div className="list-group">
        {
         data.map((item) =>
          <SwipeToDelete key={item.id}>
            <a style={inboxItemStyle({hover})}
               onPointerOver={()=> setHover(true)}
               onPointerOut={() => setHover(false)}
            >
              <h4 className="list-group-item-heading">{item.date}</h4>
              <p className="list-group-item-text">{item.text}</p>
            </a>
          </SwipeToDelete>
         )
       }
      </div>
    );
}
export default Inbox;

This is what's happening:

enter image description here

This is my expected result:

enter image description here

Why is it enabling hover on ALL <a> elements? Isn't the .map separating them into individual objects? Where am I going wrong?

I tried using index based hooks as well from THIS QUESTION

But the hover ceases to work altogether with this functionality. What Am I doing wrong?

const [hover, setHover] = useState(-1);

const showHandler = (i)=>{
    setHover(i);
}

const hideHandler=()=>{
    setHover(-1)
}

return (
   
<div className="list-group">
   {
    data.map((item, i) => (
       <SwipeToDelete key={item.id}>
           <a style={setInboxItemStyle({hover})}
               onm ouseLeave={hideHandler}
                onm ouseEnter={()=>showHandler(i)}
                 >
           <h4 className="list-group-item-heading">{item.date}</h4>
           <p className="list-group-item-text">{item.text}</p>
           </a>
       </SwipeToDelete>
    ))
   }
</div>
          
);
}

CodePudding user response:

It seems that this is a case of event bubbling. Try this:

<a style={setInboxItemStyle({hover})}
         onm ouseLeave={hideHandler}
         onm ouseEnter={(e)=>showHandler(e, i)}
             >
       ...
</a>

And then in your handler:

const showHandler = (e, i) => {
e.stopPropagation();
setHover(i);
}

CodePudding user response:

Look at this simplification of your code:

const [hover, setHover] = useState(false)
return (
<>
  <Element1 />
  <Element2 />
  <Element3 />
</>

Do you see the problem? - all 3 Elements are accesing the same state. If you hover over any of them you setHover(true) and since there is only this one state which is shared between all Elements you can't distinguish which Element is the one that is hovered over.

So to fix this problem, each Element needs their own state.

I would just create a new component (which is the repeated Element) and in that component you could have a state or hover. That way each repeated element would have their own state. So something like this:

function mappedElement (item) {
const [hover, setHover] = useState(false)
return (
<SwipeToDelete key={item.id}>
  <a
    style={inboxItemStyle({ hover })}
    onPointerOver={() => setHover(true)}
    onPointerOut={() => setHover(false)}
  >
    <h4 className="list-group-item-heading">{item.date}</h4>
    <p className="list-group-item-text">{item.text}</p>
  </a>
</SwipeToDelete>;
)}

and then your map would look something like this:

data.map(item => <mappedElement item={item} />) 

The index based hook would also be a possible solution, I guess there is an issue with the style function (inBoxItemStyle) - since now hover isn't storing information on weather or not something is hovered on anymore but on which element is being hovered on you'd have to rewrite that function/style to consider that change

CodePudding user response:

from your link above: "display={hoveredCart === i? 'block':'none'}"

take a look at this (code with your basic setup): https://codesandbox.io/embed/elastic-ritchie-ee5409?fontsize=14&hidenavigation=1&theme=dark

also you can add styles to wrapper div and use :hover

  • Related