'How to differentiate between double tap and single tap on react native?

I need to perform different action on single and double tap on a view. On double tap I need to like the image just like Instagram double tap experience. On single tap I need to open a modal. For double tap I have used TapGestureHandler which works perfect

 <TapGestureHandler
          ref={this.doubleTapRef}
          maxDelayMs={200}
          onHandlerStateChange={this.onProductImageDoubleTap}
          numberOfTaps={2}
        >
         <SomeChildComponent ...

But when I add any Touchable to detect single tap in the

 <TouchableWithoutFeedback onPress={this.imageTapped}>

on double tapping the this.imageTapped function is called twice along with this.onProductImageDoubleTap. Is there any way to cancel tap on touchable when two taps are done is quick succession



Solution 1:[1]

The best solution is not using state, since setting state is asynchronous.Works like a charm for me on android !

let lastPress = 0;
const functionalComp = () => {
    const onDoublePress = () => {
        const time = new Date().getTime();
        const delta = time - lastPress;

        const DOUBLE_PRESS_DELAY = 400;
        if (delta < DOUBLE_PRESS_DELAY) {
            // Success double press
            console.log('double press');

        }
        lastPress = time;
    };

    return <View
        onStartShouldSetResponder =
        {(evt) => onDoublePress()}>
     </View>
}

Solution 2:[2]

2022 update

This is a performant native solution without any JS thread blocking calculation! Many more tips here

const tap = Gesture.Tap()
  .numberOfTaps(2)
  .onStart(() => {
    console.log('Yay, double tap!');
  });

return (
    <GestureDetector gesture={tap}>
      {children}
    </GestureDetector>
);

Solution 3:[3]

The package react-native-double-tap seems to be what you are looking for.

Solution 4:[4]

since you are asking on handling one tap and double tap, here's a simple code i think should covered your issue

Untested

first defined clickCount:0 in state:

state={clickCount:0, //another state}

then create a function with setTimeout to handling if user tapping once or two times:

handlingTap(){
  this.state.clickCount==1 ?
  //user tap twice so run this.onProductImageDoubleTap()
  this.onProductImageDoubleTap :
  //user tap once so run this.imageTapped with setTimeout and setState
  //i set 1 sec for double tap, so if user tap twice more than 1 sec, it's count as one tap
  this.setState({clickCount:1}, ()=>{
    setTimeout(()=>{
      this.setState({clickCount:0})
      this.imageTapped()
    }, 1000)
  })
}

<TouchableWithoutFeedback onPress={this.handlingTap()}/>

just used TouchableWithoutFeedback instead of TapGestureHandler

Solution 5:[5]

With hooks:

const [lastPressed, setLastPressed] = useState(0);

const handlePress = useCallback(() => {
  const time = new Date().getTime();
  const delta = time - lastPressed;
  setLastPressed(time);
  if (lastPressed) {
    if (delta < DOUBLE_PRESS_DELAY) {
      console.log('double press');
    } else {
      console.log('single press');
    }
  }
}, [lastPressed]);

Solution 6:[6]

I have modified flix's answer into this. By this way, you can catch one and double click separately. I also changed debounce into 300ms which is fairly well for one and double click.

state={clickCount:0, //another state}

Binding context into the handlingTap method

constructor(props){
   super(props)
   this.handlingTap = this.handlingTap.bind(this)
}

With this function you can catch them separately

handlingTap() {
    this.state.clickCount === 1
        ? this.doubleClick() // This catches double click
        : this.setState(state => ({ clickCount: state.clickCount + 1 }), () => {
            setTimeout(() => {
                if (this.state.clickCount !== 2) {
                    this.oneClick() // this catches one click
                }
                this.setState({ clickCount: 0 })
            }, 300)
        })
}

In the button you can use this way

<TouchableWithoutFeedback onPress={this.handlingTap}></TouchableWithoutFeedback>

Solution 7:[7]

The best solution is use react-native-gesture-handler

https://github.com/software-mansion/react-native-gesture-handler

Here is my solution -

import {State, TapGestureHandler} from 'react-native-gesture-handler';
export const DoubleTap = ({children}: any) => {
  const doubleTapRef = useRef(null);

  const onSingleTapEvent = (event: any) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      console.log("single tap 1");
    }
  };

  const onDoubleTapEvent = (event: any) => {
    if (event.nativeEvent.state === State.ACTIVE) {
       console.log("double tap 1");
    }
  };

  return (
    <TapGestureHandler
      onHandlerStateChange={onSingleTapEvent}
      waitFor={doubleTapRef}>
      <TapGestureHandler
        ref={doubleTapRef}
        onHandlerStateChange={onDoubleTapEvent}
        numberOfTaps={2}>
        {children}
      </TapGestureHandler>
    </TapGestureHandler>
  );
};

Now we will wrap the component where we need to detect the double and single tap : -

 <DoubleTap>
    <View>
      ...some view and text
    </View>
 </DoubleTap>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 shaunak1111
Solution 2 Jácint Varga
Solution 3 Tuur155
Solution 4 flix
Solution 5 MarvinVK
Solution 6 cangokceaslan
Solution 7 Kanishka Sahu