react: onMouseLeave doesn't work if the node gets detached
I have a problem with this kind of component:
class onHover extends Component {
constructor(props) {
super(props);
this.state = {
bool: false,
}
}
render() {
return (
<div onMouseEnter={() => this.setState({ bool: true })} onMouseLeave={() => this.setState({ bool: false })}>
{
this.state.bool ? (
<span>[OPTION1] show after onMouseEnter</span>
) : (
<div>[OPTION2] show after onMouseLeave</div>
)
}
</div>
)
}
}
Notice that the first option1 is a span, option2 is a div.
This works fine when I move the mouse slowly.
Though, if I “cut” through this with the mouse very fast, only the onMouseEnter event gets triggered, but not the onMouseLeave event.
It is always working though, if both options have the same tag (if both are div or both are span).
EDIT: I think it has something to do with rerendering. When the components are of the same type, but I force a rerender, it causes the same issues.
class onHover extends Component {
constructor(props) {
super(props);
this.state = {
bool: false,
}
}
render() {
return (
<div onMouseEnter={() => this.setState({ bool: true })} onMouseLeave={() => this.setState({ bool: false })}>
{
this.state.bool ? (
<div key={Math.random()}>[OPTION1] show after onMouseEnter</div>
) : (
<div key={Math.random()}>[OPTION2] show after onMouseLeave</div>
)
}
</div>
)
}
}
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 14
- Comments: 23 (1 by maintainers)
Reopening per conversation with @sophiebits who pointed out we might be able to fix it by not relying on bubbling.
Any news on this issue? As far as I see, this makes it almost impossible to write performant code with requestAnimationFrame or throttling for mouse move events. So it seems quite relevant.
Couldn’t you write something like so:
It looks a little bit cleaner in the return statement and I’m pretty sure you’ll get the same effect you’re looking for. Also try reading this section of React docs Conditional Rendering
What kind of news are you expecting? The browsers aren’t consistent about firing events on deleted elements. I don’t think it’s something we can fix in React, but happy to hear suggestions.
For now I’ll close as I don’t see this issue is actionable for us. If you want to avoid such problems, don’t remove/replace the hovered elements during hover.
@mrdanimal @pybuche et al. in case you are still struggling, I’ve just published a hook that takes care of this: https://github.com/mjsarfatti/use-mouse-leave/
I had a similar problem as yours, and the hook is working well and reliably for me. If you use it please let me know if you run in any problem!
The problem seems to be with React’s synthetic event system, as it is inconsistent for the mouseenter/leave events. I don’t know why react decided to redefine the semantics of those events, but they are more useful in their original form. If we rely on the browser’s event system, things work a lot more consistent, it’s a lot harder to get a missing mouseleave event; though not impossible in chrome today.
It’s as simple as manually adding the event listeners before rendering the DOM:
Hope this helps others.
Your idea looks cool but it’s using the same
onMouseLeaveas I was and I assume it would still fail on a fast mouse movement, so theboolparameter insidethis.statewould remaintrueand the mouse would not be hovering the element.I solved it by using
styled-components.Having same issue.
@gaearon
How is this achievable? I am using render props and render conditionally one or another depending on hover state.
Thanks!
#4492 related I think.
Anyway, it’s simply because the element emitting the event is replaced and it messes up the events. I could be wrong, but IIRC there’s also some weird browser/spec thing involved in this behavior too (just saying).