React Onmouseenter And Onmouseleave Not Behaving Consistently
Solution 1:
Issue
I think the main issue with your implementation is with the way asynchronous event callbacks are queued up and processed in the event loop. I can't find any hard details about the latency of processing event callbacks but the docs here and here may shed some more light on the matter if you care to do a deep dive.
Basically the issue is two-fold:
- There is a minute duration a single event loop takes to process, i.e. detect an event and add it to the queue. I suspect the mouse is moving fast enough off/out the screen or into another div it isn't detected. The divs "jumping"/"moving" when hovering also doesn't help much.
- The component logic assumes all events can and will be detected and simply toggled the previous existing state. As soon as an event is missed though the toggling is inverted, thus the issue you see. Even in the updated sandbox this latency can cause one of the elements to get "stuck" hovered
Proposed Solution
Add a mouse move event listener to the window object and check if the mouse move event target is contained by one of your elements. If not currently hovered and element contains event target, set isHovered
true, and if currently hovered and the element does not contain event target, set isHovered
false.
This isn't a full replacement for the enter/leave|over/out event listeners attached to the containing div
as I was still able to reproduce an edge-case. I noticed your UI is most susceptible to this issue when moving the mouse quickly and leaving the window.
Combining the window and div event listeners gives a pretty good resolution (though I was still able to reproduce edge-case it is much more difficult to do). What also seems to have helped a bit is not defining anonymous callback functions for the div.
importReact, { createRef } from"react";
exportdefaultclassGeoextendsReact.Component {
state = {
isHovering: false
};
mouseMoveRef = createRef();
componentDidMount() {
window.addEventListener("mousemove", this.checkHover, true);
}
componentWillUnmount() {
window.removeEventListener("mousemove", this.checkHover, true);
}
setHover = () =>this.setState({ isHovering: true });
setUnhover = () =>this.setState({ isHovering: false });
checkHover = e => {
if (this.mouseMoveRef.current) {
const { isHovering } = this.state;
const mouseOver = this.mouseMoveRef.current.contains(e.target);
if (!isHovering && mouseOver) {
this.setHover();
}
if (isHovering && !mouseOver) {
this.setUnhover();
}
}
};
render() {
var textDisplay;
if (this.state.isHovering) {
textDisplay = <span>HOVERING</span>;
} else {
textDisplay = <h1>NOT HOVERING</h1>;
}
return (
<divref={this.mouseMoveRef}onMouseEnter={this.setHover}onMouseLeave={this.setUnhover}style={{width:300, height:100, background: "green" }}
>
{textDisplay}
</div>
);
}
}
Solution 2:
As far as I can see, you have a problem with the way you update the state. Bear in mind that React may update the state asynchronously.
Changing toggleHoverState function will solve the issue
toggleHoverState() {
this.setState(state => ({isHovering: !state.isHovering}));
}
Go to this section in React docs for more info
Post a Comment for "React Onmouseenter And Onmouseleave Not Behaving Consistently"