React: On double click, the single click event is fired (but shoudln't)

Created on 19 Feb 2015  路  16Comments  路  Source: facebook/react

<div onDoubleClick={@_onRowDoubleClick.bind(null, index)} onClick={@_onRowClick}>

With the above code, both event handlers are called when the div element is double clicked. But it should not call the single click event handler.

Any chance this can be fixed?

Thanks

Most helpful comment

You can simulate the doubleclick handler, but unfortunately it means that your single clicks will be slower to fire because you have to wait to see if the user action is actually a doubleclick.

Something like:

function getClickHandler(onClick, onDblClick, delay) {
    var timeoutID = null;
    delay = delay || 250;
    return function (event) {
        if (!timeoutID) {
            timeoutID = setTimeout(function () {
                onClick(event);
                timeoutID = null
            }, delay);
        } else {
            timeoutID = clearTimeout(timeoutID);
            onDblClick(event);
        }
    };
}

http://jsfiddle.net/ye3bgs3x/1/

All 16 comments

I'm quite certain this is correct as-is (but not 100%), click stands for click, not single-click.

@syranide Yup. The vanilla click events will happen as usual, but an additional dblclick event is dispatched if the clicks happen within a certain timeframe.

@binarykitchen If you have another interaction paradigm you can use for that feature, I'd recommend exploring it. Double click support isn't worth the annoyance from personal experience.

I see, onClick stands for clicks, not single clicks. But what if I want to distinguish between single and double clicks?

@yaycmyk I understand and agree that double clicks are unusual but I am working on a large UI where double clicks are very important. We cannot change that behaviour, otherwise many customers get confused. We are currently migrating to React and it would be sad if there is no double click support.

But what if I want to distinguish between single and double clicks?

Not trying to be snark :), but that's something you'll have to take up with browser vendors/W3, React is simply doing it's best to normalize according to the defined standards.

If you're able to achieve this distinction yourself with native events then you're free to bind them manually and wrap it up in a reusable component. It's not perfect but it's very close.

@binarykitchen I suppose you could set a timer on your single clicks and see if a double click event happens before n ms... but then you'd be artificially slowing down your UI (perhaps imperceptibly) by forcing single clicks to wait.

But anyway, React is doing it's job as @syranide said... so no bug here.

Good point @syranide

Let's say I want to add two new event handlers called _onRowSingleClick and _onRowDoubleClick on a reusable table, how can I do that? I mean, are there React-based examples out there adding custom events?

I already wrote this hack in Coffee to distinguish between these two events. Figuring out how I can hide this in a parent class and have all other React components inherit from this:

  _onRowClick: (e, index) ->

    clickCounter = @state.clickCounter

    if !clickCounter[index]
      clickCounter[index] = 0
      @setState(clickCounter: clickCounter)

    clickCounter[index]++

    @setState(clickCounter: clickCounter)

    if clickCounter[index] == 1
      target = e.target

      setTimeout(=>
        isSingle = if clickCounter[index] == 1 then true else false

        clickCounter[index] = 0
        @setState(clickCounter: clickCounter)

        if isSingle
          @_onRowSingleClick(target)
        else
          @_onRowDoubleClick(index)

      , 200)

You can simulate the doubleclick handler, but unfortunately it means that your single clicks will be slower to fire because you have to wait to see if the user action is actually a doubleclick.

Something like:

function getClickHandler(onClick, onDblClick, delay) {
    var timeoutID = null;
    delay = delay || 250;
    return function (event) {
        if (!timeoutID) {
            timeoutID = setTimeout(function () {
                onClick(event);
                timeoutID = null
            }, delay);
        } else {
            timeoutID = clearTimeout(timeoutID);
            onDblClick(event);
        }
    };
}

http://jsfiddle.net/ye3bgs3x/1/

@binarykitchen You can bind manually in componentDidMount/Update.

Thanks, I figured this out with a new parent component, dealing with double clicks.

@binarykitchen could you post your solution?

Can somebody please explain why https://github.com/facebook/react/issues/3185#issuecomment-75138124 is thumbed down by people without giving any reason?

folderOpenOnOff = () => {
        this.isClicked = !this.isClicked
        setTimeout(() => {
            if(this.isClicked) {
                this.isClicked = false;
                this.setState({ isOpen: !this.state.isOpen}); //Whatever you want on ClickEvent
            }
        }, 300)
    }

This is onClickHandler, and I defined onDoubleClickHandler seperatly.

I'm guessing people downvoted https://github.com/facebook/react/issues/3185#issuecomment-75138124 because they don't realize that there can be no blanket solution that does not require a timeout.

But how could it be otherwise? The single click listener cannot see into the future.

Here is my solution:

In your functional component, create a state variable:

const [clicks, setClicks] = useState(0);

Add this useEffect block to it:

  useEffect(() => {
    let singleClickTimer;
    if (clicks === 1) {
      singleClickTimer = setTimeout(function() {
        doSingleClickThing()
        setClicks(0);
      }, 250);
    } else if (clicks === 2) {
        doDoubleClickThing()
        setClicks(0);
    }
    return () => clearTimeout(singleClickTimer);
  }, [clicks]);

And increment the clicks on each onClick event of your element:

<div onClick={() => setClicks(clicks + 1)}>

@scaryguy Nice, thank you, but what about event in doSingleClickThing and doDoubleClickThing?

Was this page helpful?
0 / 5 - 0 ratings