Подтвердить что ты не робот

OnClick работает, но onDoubleClick игнорируется в компоненте React

Я создаю игру Minesweeper с React и хочу выполнить другое действие, когда ячейка одинарная или двойная. В настоящее время функция onDoubleClick никогда не срабатывает, отображается предупреждение из onClick. Если я удаляю обработчик onClick, onDoubleClick работает. Почему оба события не работают? Возможно ли иметь оба события на элементе?

/** @jsx React.DOM */

var Mine = React.createClass({
  render: function(){
    return (
      <div className="mineBox" id={this.props.id} onDoubleClick={this.props.onDoubleClick} onClick={this.props.onClick}></div>
    )
  }
});

var MineRow = React.createClass({
  render: function(){
    var width = this.props.width,
        row = [];
    for (var i = 0; i < width; i++){
      row.push(<Mine id={String(this.props.row + i)} boxClass={this.props.boxClass} onDoubleClick={this.props.onDoubleClick} onClick={this.props.onClick}/>)
    }
    return (
      <div>{row}</div>
    )
  }
})

var MineSweeper = React.createClass({
  handleDoubleClick: function(){
    alert('Double Clicked');
  },
  handleClick: function(){
    alert('Single Clicked');
  },
  render: function(){
    var height = this.props.height,
        table = [];
    for (var i = 0; i < height; i++){
      table.push(<MineRow width={this.props.width} row={String.fromCharCode(97 + i)} onDoubleClick={this.handleDoubleClick} onClick={this.handleClick}/>)
    }
    return (
      <div>{table}</div>
    )
  }
})

var bombs = ['a0', 'b1', 'c2'];
React.renderComponent(<MineSweeper height={5} width={5} bombs={bombs}/>, document.getElementById('content'));
4b9b3361

Ответ 1

Это не ограничение React, это ограничение событий DOM click и dblclick. Как было предложено Quirksmode щелкните документацию:

Не регистрируйте события click и dblclick для одного и того же элемента: невозможно выделить события с одним щелчком мыши из событий кликов, которые приводят к событию dblclick.

Для более точной документации спецификация W3C в событии dblclick гласит:

Пользовательский агент должен отправить это событие, когда основная кнопка указательного устройства дважды нажата над элементом.

Событие с двойным щелчком обязательно происходит после двух кликов.

Edit

Еще одно предлагаемое чтение: jQuery dblclick обработчик:

Не рекомендуется привязывать обработчики к событиям щелчка и dblclick для одного и того же элемента. Последовательность инициируемых событий варьируется от браузера к браузеру, причем некоторые из них получают два события щелчка перед dblclick, а другие - только одно. Чувствительность к двойному щелчку (максимальное время между щелчками, которое определяется как двойной щелчок) может варьироваться в зависимости от операционной системы и браузера и часто настраивается пользователем.

Ответ 2

Требуемый результат может быть достигнут за счет очень небольшой задержки при отмене обычного действия щелчка, который будет отменен, когда произойдет событие двойного щелчка.

  let timer = 0;
  let delay = 200;
  let prevent = false;

  doClickAction() {
    console.log(' click');
  }
  doDoubleClickAction() {
    console.log('Double Click')
  }
  handleClick() {
    let me = this;
    timer = setTimeout(function() {
      if (!prevent) {
        me.doClickAction();
      }
      prevent = false;
    }, delay);
  }
  handleDoubleClick(){
    clearTimeout(timer);
    prevent = true;
    this.doDoubleClickAction();
  }
 < button onClick={this.handleClick.bind(this)} 
    onDoubleClick = {this.handleDoubleClick.bind(this)} > click me </button>

Ответ 3

Edit:

Я обнаружил, что это не проблема с React 0.15.3.


Оригинал:

Для Реагирования 0.13.3 здесь два решения.

1. ref callback

Обратите внимание, что даже в случае двойного щелчка обработчик с одним кликом будет вызываться дважды (один раз для каждого клика).

const ListItem = React.createClass({

  handleClick() {
    console.log('single click');
  },

  handleDoubleClick() {
    console.log('double click');
  },

  refCallback(item) {
    if (item) {
      item.getDOMNode().ondblclick = this.handleDoubleClick;
    }
  },

  render() {
    return (
      <div onClick={this.handleClick}
           ref={this.refCallback}>
      </div>
    );
  }
});

module.exports = ListItem;

2. lodash debounce

У меня было другое решение, которое использовало lodash, но я отказался от него из-за сложности. Преимущество этого заключалось в том, что "click" был вызван только один раз, а вовсе не в случае "двойного щелчка".

import _ from 'lodash'

const ListItem = React.createClass({

  handleClick(e) {
    if (!this._delayedClick) {
      this._delayedClick = _.debounce(this.doClick, 500);
    }
    if (this.clickedOnce) {
      this._delayedClick.cancel();
      this.clickedOnce = false;
      console.log('double click');
    } else {
      this._delayedClick(e);
      this.clickedOnce = true;
    }
  },

  doClick(e) {
    this.clickedOnce = undefined;
    console.log('single click');
  },

  render() {
    return (
      <div onClick={this.handleClick}>
      </div>
    );
  }
});

module.exports = ListItem;

на мыльной коробке

Я ценю мысль о том, что двойной щелчок не является чем-то легко обнаруживаемым, но к лучшему или худшему он является парадигмой, которая существует, и тем, что пользователи понимают из-за ее распространенности в операционных системах. Кроме того, это парадигма, которую поддерживают современные браузеры. Мое мнение заключается в том, что до тех пор, пока он не будет удален из спецификаций DOM, React должен поддерживать поддерживающий onDoubleClick prop рядом с onClick. К сожалению, кажется, что они этого не делают.

Ответ 4

Вот что я сделал. Любые предложения по улучшению приветствуются.

class DoubleClick extends React.Component {
  state = {counter: 0}

  handleClick = () => {
   this.setState(state => ({
    counter: this.state.counter + 1,
  }))
 }


  handleDoubleClick = () => {
   this.setState(state => ({
    counter: this.state.counter - 2,
  }))
 }

 render() {
   return(
   <>
    <button onClick={this.handleClick} onDoubleClick={this.handleDoubleClick>
      {this.state.counter}
    </button>
   </>
  )
 }
}