如何针对React元素测试event.target?

时间:2018-09-30 15:25:37

标签: javascript reactjs

我是React的新手,想通过练习来学习。我正在尝试实现一个可拖动组件,该组件可包装一个子元素并捕获鼠标事件,然后触发从props传递过来的函数,从而依次更新状态(和相关的CSS)。

我的可拖动组件如下:

import React from "react";
import PropTypes from "prop-types";

export default class Draggable extends React.Component {
  constructor(props) {
    super(props);
    this.mouseMove = this.mouseMove.bind(this);
    this.mouseUp = this.mouseUp.bind(this);
    this.beginDrag = this.beginDrag.bind(this);
    this.state = {
      isDragging: false,
      lastX: 0,
      lastY: 0
    };
  }

  static propTypes = {
    onDrag: PropTypes.func.isRequired,
    children: PropTypes.element.isRequired
  };

  render() {
    const child = React.Children.only(this.props.children);
    const newChild = React.cloneElement(child, {
      onMouseDown: this.beginDrag
    });

    return newChild;
  }

  componentDidMount() {
    window.addEventListener("mousemove", this.mouseMove.bind(this));
    window.addEventListener("mouseup", this.mouseUp.bind(this));
  }

  componentWillUnmount() {
    window.removeEventListener("mousemove", this.mouseMove);
    window.removeEventListener("mouseup", this.mouseUp);
  }

  mouseMove(e) {
    if (this.state.isDragging) {
      const deltas = {
        x: e.clientX - this.state.lastX,
        y: e.clientY - this.state.lastY
      };

      this.setState({
        lastX: e.clientX,
        lastY: e.clientY
      });

      this.props.onDrag(deltas);
    }
  };

  mouseUp() {
    if (this.state.isDragging) {
      this.setState({
        isDragging: false
      });
    }
  };

  beginDrag(e) {
    this.setState({
      isDragging: true,
      lastX: e.clientX,
      lastY: e.clientY
    });
  };
}

示例用法:

import React from "react";
import Draggable from "./Draggable";

export default class SomeComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      translateX: 0,
      translateY: 0
    };
  }

  render() {
    const style = {
      top: this.state.translateY,
      left: this.state.translateX
    };

    return (
      <Draggable onDrag={this.onDrag}>
        <div className="someComponent">
          <div className="dragContainer" style={style}>
            <div className="block">
              hello world
            </div>
            <div className="block">
              hello world
            </div>
            <div className="block">
              hello world
            </div>
          </div>
        </div>
      </Draggable>
    );
  }

  onDrag = (deltas) => {
    this.setState(state => ({
      translateX: state.translateX + deltas.x,
      translateY: state.translateY + deltas.y
    }));
  };
}

相关的SCSS:

.someComponent {
  display: block;
  width: 100%;
  height: 100%;
  position: relative;

  .dragContainer {
    position: absolute;
  }

  .block {
    display: inline-block:
  }
}

问题

在我拖动容器的意义上,拖动确实起作用,.dragContainer的内容将按预期进行翻译。

问题是,当我开始从任何子元素(例如.block之一)中拖动时,拖动也起作用。我可以通过在e.target中测试Draggable.beginDrag来缓解这种情况,但是我不知道该如何进行测试。理想情况下,我将e.targetprops中传递的子元素进行比较,但是由于它是虚拟DOM,所以我敢肯定它不会起作用。

我应该将e.target与什么进行比较?我已经读过React.createRef,但不确定在这种情况下如何使用它。

1 个答案:

答案 0 :(得分:0)

e.target应该允许访问被单击的DOM节点。为什么不将event.target.id与您为每个块提供的ID进行比较?然后,您可以根据比较结果执行所需的操作。