Prevent click events on double click with React (with and without Hooks)

Ceci García García
Trabe
Published in
3 min readJul 22, 2019
Photo by Bonnie Kittle on Unsplash

This post proposes two implementations (one with a HOC, and the other one with a Hook) that solve the problem of React triggering the onClick event twice before triggering the onDoubleClick when a pointing device is double-clicked.

If you already know about this problem, you can skip the next section and jump directly to the solution.

Handling both onDoubleClick and onClick events

React allows us to handle DOM events in React elements. Equivalent to click event and dblclick event, in React we can configure, for an element:

  • onClick: callback that will be executed when a click event is triggered.
  • onDoubleClick: callback that will be executed when a dblclick event is triggered.

Look at the example below of a <ClickableBox> component that allows the configuration of both handlers:

If we make a double-click on the <ClickableBox>, we get this in the browser console:

Like browsers, React triggers two single click events before triggering the doubleclick event.

How can we avoid the click event to be executed twice when a doubleclick happens?

Solution

The solution lies in delaying the execution of the onClick handler until we know that the click is not part of a double-click: The onClick handlers are delayed for a while in a queue of pending promises and, when a dblclick event is triggered, all the pending promises are cancelled so they will never be executed.

Cancellable promise

To handle the pending promises, we will use cancellable promises. I’ve already talked about the cancellable promises in a the post “Avoid updates on unmounted React components”, you can check it out if you haven’t!

A cancellable promise is, basically, a wrapped promise that can be cancelled (rejected with extra info to know the rejection happened because of a cancellation). Below the implementation:

Below the tools we are going to import from /utils in the following implementations:

Solution using a HOC

There’re some notes all over the implementation to make it easier to follow:

We can use pleaseStopTriggeringClicksOnDoubleClick HOC with any component:

Solution using Hooks

With Hooks, we can extract to a custom Hook both logics: handling cancellable promises and redefining onClick and onDoubleClick handlers.

To extract the logic of handling cancellable promises, we define a custom Hook that holds a ref with the array of pending promises, defines the api to interact with this array, and returns the api.

We don’t store the pending promises in a state variable because we don’t want the component to be re-rendered every time this array changes.

To extract the logic of redefining onClick and onDoubleClick handlers, we define another custom Hook that, given both handlers, redefine them to implement the solution described above using the useCancellablePromises Hook:

Any functional component can use the useClickPreventionOnDoubleClick Hook easily:

Summing up

If you want to handle both onClick and onDoubleClick, you will probably need to control the onClick handler to not be triggered when the click event comes from a dblClick event. You can use the solution proposed in this post choosing the one that suits you best, the one with a HOC for your class components or the one using custom Hooks for your functional components.

--

--