Prevent click events on double click with React (with and without Hooks)
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
onClick: callback that will be executed when a
clickevent is triggered.
onDoubleClick: callback that will be executed when a
dblclickevent 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
How can we avoid the
click event to be executed twice when a
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.
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
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
onDoubleClick handlers, we define another custom Hook that, given both handlers, redefine them to implement the solution described above using the
Any functional component can use the
useClickPreventionOnDoubleClick Hook easily:
If you want to handle both
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.