javascriptreactjs

onClick works but onDoubleClick is ignored on React component


I am building a Minesweeper game with React and want to perform a different action when a cell is single or double clicked. Currently, the onDoubleClick function will never fire, the alert from onClick is shown. If I remove the onClick handler, onDoubleClick works. Why don't both events work? Is it possible to have both events on an element?

/** @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'));

Solution

  • Edit:

    I've found that this is not an issue with React 0.15.3.


    Original:

    For React 0.13.3, here are two solutions.

    1. ref callback

    Note, even in the case of double-click, the single-click handler will be called twice (once for each click).

    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

    I had another solution that used lodash, but I abandoned it because of the complexity. The benefit of this was that "click" was only called once, and not at all in the case of "double-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;
    

    on the soapbox

    I appreciate the idea that double-click isn't something easily detected, but for better or worse it IS a paradigm that exists and one that users understand because of its prevalence in operating systems. Furthermore, it's a paradigm that modern browsers still support. Until such time that it is removed from the DOM specifications, my opinion is that React should support a functioning onDoubleClick prop alongside onClick. It's unfortunate that it seems they do not.