Handling double tap on screen is not a secret, but sometimes like displaying a video, we want to handle double and single taps, on single we pause/play video and double we want to perform a like action on that video.
Using Gesture Handler
The most out of the box solution for double tap, probably is using react-native-gesture-handler library and passing a callback on TapGestureHandler component:
<GestureHandlerRootView>
<TapGestureHandler
onHandlerStateChange={handleDoubleTap}
numberOfTaps={2}>
<YourTapableComponent/>
</TapGestureHandler>
</GestureHandlerRootView>
But if you want to handle both cases, double and single tap, we need to start to add another handler HOC:
const doubleTapRef = useRef();
{...}
<GestureHandlerRootView>
<TapGestureHandler
onHandlerStateChange={handleSingleTap}
waitFor={doubleTapRef}>
<TapGestureHandler
onHandlerStateChange={handleDoubleTap}
numberOfTaps={2}
ref={doubleTapRef}>
<YourTapableComponent/>
</TapGestureHandler>
</TapGestureHandler>
</GestureHandlerRootView>
In this case we added a reference so the single tap will wait for the referred component to resolve.
Also there is one more thing, our handlers should check the event state:
import {
GestureHandlerRootView,
HandlerStateChangeEvent,
State,
TapGestureHandler,
TapGestureHandlerEventPayload,
} from 'react-native-gesture-handler';
{...}
const handleSingleTap = (
e: HandlerStateChangeEvent<TapGestureHandlerEventPayload>,
) => {
if (e.nativeEvent.state === State.ACTIVE) {
console.log('sigle tapped');
}
};
Coding your solution
The first time I had to implement double/single tap, gesture handler was my choice, but after a certain time I realized that I could do it without the help of a library.
To achieve this we will use the concept of debounce, but passing two functions as params. Basically a debounce will retard the execution of a function and if it calls again we clean the timeout and schedule a new one. But in our case if timer already exists we will clean it and call the double tap callback, otherwise the timeout will execute the single tap callback.
let timer = null;
const TIMEOUT = 500
const debounce = (onSingle, onDouble) => {
if (timer) {
clearTimeout(timer);
timer = null;
onDouble();
} else {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
onSingle();
}, TIMEOUT);
}
};
The amount of time passed on TIMEOUT is the time you expect for a double tap, it can be customized to less than that, gesture handler uses by default 300ms.
Here is a full example:
Conclusion
Both cases are more than capable to handle double and single taps, if I need to implement it again, I will do the second approach, in my opinion looks more clean and easily can be refactored into a HOC component and handle the same case in different parts of the app.
I hope this tutorial can be useful for you, and any comment, suggestion or feedback are wellcome.