1

我有多个(超过 15 个)div标签作为图块。如果鼠标悬停在它上面,我需要强调每一个。所以每个标签都有onMouseEnter/Leave如下功能。

<div
    key={key}
    onMouseEnter={onMouseEnter(key)}
    onMouseLeave={onMouseLeave(key)}
>
    ...
</div>

我还将每个图块键放在Map数据结构中。

const onMouseEnter = key => {
    return function() {
        const newIsHover = new Map(isHover)
        newIsHover.set(key, true)
        setIsHover(newIsHover)
    }
}

const onMouseLeave = key => {
    return function() {
        const newIsHover = new Map(isHover)
        newIsHover.delete(key)
        setIsHover(newIsHover)
    }
}

由于组件是钩子,它把它的状态放在一个useState.

const [isHover, setIsHover] = useState(new Map())

这里发生了什么:

  • 我总是输入一个图块:onMouseEnter调用函数并将其键添加到地图(如预期的那样)
  • 当我离开瓷砖时:总是onMouseLeave被调用,但有时键被移除(如预期的那样)并且瓷砖变回其正常形状但有时它没有(问题就在这里,在这种情况下地图更新setIsHoveronMouseLeave但它在组件中没有改变!)。

我认为地图已按预期更新,但当我移动新瓷砖时,它还不明白这一点。所以它用它所拥有的覆盖它。

PS:添加示例。在瓷砖之间高速移动!

4

1 回答 1

3

与基于类的组件一样,对更新状态的调用是异步的并且需要排队。尝试使用功能状态更新来确保这些排队更新正确更新之前的状态。setIsHover这应该修复使用相同键的快速连续调用之间的竞争条件。

请注意,如果您在它们正确突出显示和取消突出显示的图块之间移动得足够慢,但更快(如滑动)并且 2 个或更多可能会卡住,直到您再次缓慢退出图块。

const onMouseEnter = key => {
  return function() {
    setIsHover(prevIsHover => {
      const newIsHover = new Map(prevIsHover);
      newIsHover.set(key, true);
      return newIsHover;
    });
  }
}

const onMouseLeave = key => {
  return function() {
    setIsHover(prevIsHover => {
      const newIsHover = new Map(prevIsHover);
      newIsHover.delete(key);
      return newIsHover;
    });
  }
}

编辑瓷砖-良好状态更新

但我应该注意到,对于简单地应用一些组件样式,尤其是悬停,这是一项繁重的工作。它可以更简单地使用 CSS 来实现。

tileStyles.css

.tile {
  background-color: lightgray;
  border: 3px solid black;
  height: 100px;
  line-height: 100px;
  margin: 10px;
  text-align: center;
  width: 100px;
}

.tile:hover {
  border-color: red;
}

瓦片.jsx

import React from "react";
import { withStyles } from "@material-ui/core";

import "./tileStyles.css";

const styles = {
  container: { display: "flex", width: "600px", flexWrap: "wrap" }
};

const Tiles = ({ classes: { container }, tiles }) => {
  return (
    <div className={container}>
      {tiles.map((tl, key) => {
        return (
          <div className="tile" key={key} name={key}>
            hi
          </div>
        );
      })}
    </div>
  );
};

export default withStyles(styles)(Tiles);

正常悬停样式一起应用(同时),CSS/html 将在悬停与否时进行管理。该组件不再需要事件侦听器,也不需要维护内部状态。

编辑图块

解释

什么意思是“......对更新状态的调用是异步的并排队。”?

当您调用this.setStateuseState更新函数时,更新不会立即同步发生,而是在当前渲染周期内排队,并按照排队的顺序进行批处理。也许这个演示将有助于说明会发生什么。使这个问题感到困惑的是事件处理也是异步的,这意味着,当事件发生时,它们注册的回调被放置在要处理的事件队列中。

于 2020-03-02T15:26:11.787 回答