反应onMouseEnter和onMouseLeave行为不一致

时间:2020-08-03 00:40:09

标签: javascript reactjs

我有一个div,如果光标悬停在div上,它应该显示为“ HOVERING”,否则显示为“ NOT HOVERING”。由于某种原因,如果我慢慢将鼠标悬停在页面上的每个div上,它的行为就会与预期的一样;但是,如果我在屏幕上快速移动光标,则某些div会切换。意思是,当我的光标移到div上方时,它们将显示“ NOT HOVERING”;当我的光标不在div上时,它们将显示“ HOVERING”。

Chrome和Safari中均会发生此错误。

沙箱:

https://codesandbox.io/s/aged-butterfly-r2g6x?file=/src/Geo.js

将光标快速移到各个框上以查看问题。

2 个答案:

答案 0 :(得分:1)

问题

我认为您的实现的主要问题在于异步事件回调在event loop中排队和处理的方式。我找不到有关处理事件回调的延迟的任何硬性详细信息,但是如果您想进行深入研究,文档herehere可能会为您提供更多信息。

基本上,这个问题有两个方面:

  1. 单个事件循环需要一分钟的时间来处理,即检测一个事件并将其添加到队列中。我怀疑鼠标从屏幕移出/移入另一个div的速度足够快,无法检测到。悬停时div的“跳跃” /“移动”也无济于事。
  2. 组件逻辑假定可以并且将检测到所有事件,并简单地切换先前的现有状态。一旦错过了事件,但切换却倒转了,因此您会看到此问题。即使在更新的沙箱中,此延迟也会导致其中一个元素“卡住”徘徊

建议的解决方案

将鼠标移动事件侦听器添加到窗口对象,并检查鼠标移动事件目标是否包含在您的元素之一中。如果当前未悬停并且元素包含事件目标,则将isHovered设置为true;如果当前已悬停并且元素包含事件目标,则将isHovered设置为false。

这不能完全替代附加到包含的div上的enter / leave | over / out事件侦听器,因为我仍然能够重制边缘情况。我注意到当快速移动鼠标并离开窗口时,您的UI最容易受到此问题的影响。

将window和div事件侦听器组合在一起可以提供很好的解析度(尽管我仍然能够重现边缘情况,这很难做到)。似乎也有所帮助的是没有为div定义匿名回调函数。

import React, { createRef } from "react";

export default class Geo extends React.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 (
      <div
        ref={this.mouseMoveRef}
        onMouseEnter={this.setHover}
        onMouseLeave={this.setUnhover}
        style={{ width: 300, height: 100, background: "green" }}
      >
        {textDisplay}
      </div>
    );
  }
}

Edit friendly-chatterjee-xx2ms

答案 1 :(得分:0)

据我所知,您更新状态的方式存在问题。请记住,React可以异步更新状态。

更改toggleHoverState函数将解决此问题

lst_a = ['it is an', 'example of', 'an english simple sentence', 'if time permits', 'I will learn', 'this weekend', 'but do not', 'count on me']
lst_b = ['It', 'is', 'an', 'example', 'of', 'an', 'english', 'simple', 'sentence.', 'If', 'time', 'permits,', 'I', 'will', 'learn', 'this', 'weekend', 'but', 'do', 'not', 'count', 'on', 'me']
lst_c = ['It is an', 'example of', 'an english simple sentence.', 'If time permits,', 'I will learn', 'this weekend', 'but do not', 'count on me']

转到this section in React docs了解更多信息