Skip to content

Instantly share code, notes, and snippets.

@mrzmyr
Created November 22, 2015 16:56
Show Gist options
  • Star 49 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save mrzmyr/9ac94ca4622c1602a2a3 to your computer and use it in GitHub Desktop.
Save mrzmyr/9ac94ca4622c1602a2a3 to your computer and use it in GitHub Desktop.
React Native - Detect Double Tap
var Index = React.createClass({
getInitialState: function () {
return {
lastPress: 0
}
},
onPress: function () {
var delta = new Date().getTime() - this.state.lastPress;
if(delta < 200) {
// double tap happend
}
this.setState({
lastPress: new Date().getTime()
})
},
render: function() {
return (
<TouchableHighlight onPress={this.onPress}></TouchableHighlight>
)
}
});
@h-marvin
Copy link

Thanks for this!

@iksent
Copy link

iksent commented Jan 9, 2018

setState is asynchronous method, so it's better to use "this", not "state".

onDoublePress = (date) => {
       	const time = new Date().getTime();
	const delta = time - this.lastPress;

	const DOUBLE_PRESS_DELAY = 400;
	if (delta < DOUBLE_PRESS_DELAY) {
		// Success double press
	}
	this.lastPress = time;
};

@zen0wu
Copy link

zen0wu commented Jan 19, 2018

Agree with @iksent. The issue with using state is it'll trigger a re-render, which in a complicated view it's not going to be efficient.

@christianwish
Copy link

Thanks for the snippet.

@ahmetardal
Copy link

Thanks!

@selmi-karim
Copy link

try this new component:
A Component for Double Click based on TouchableOpacity Wrapper.
https://www.npmjs.com/package/rn-double-click

@atopus
Copy link

atopus commented Aug 13, 2018

👍 Thanks !

@Aprisyta
Copy link

Hey this is really smart. Thanks!

@code-matt
Copy link

code-matt commented Dec 27, 2018

How to prevent the single tap action though ? :( setTimeout is really unreliable in RN it seems

Edit: nevermind, its only in debug setTimeout does not behave

@nguyenvanphuc2203
Copy link

thank for the snippet

@yanickrochon
Copy link

yanickrochon commented Mar 17, 2020

From this post, I came up with this, and it works great.

const MultiTapOverlay: () => ReactNode = ({
  onLongPress,
  onMultiTaps,
  multiTapCount = 4,
  multiTapDelay = 300,
  children
}) => {
  const [lastPress, setLastPress] = useState(null);
  const [tapCount, setTapCount] = useState(0);

  const handlePress = () => {
    const now = Date.now();

    setLastPress(now);
    if (now - lastPress <= multiTapDelay) {
      if (tapCount < multiTapCount - 1) {
        setTapCount(tapCount + 1);
      } else {
        setTapCount(0);

        onMultiTaps && onMultiTaps();
      }
    } else {
      setTapCount(1);
    }
  };
  const handleLongPress = () => onLongPress && onLongPress();

  return (
    <TouchableOpacity
      delayLongPress={ 1000 }
      activeOpacity={ 0.8 }
      onLongPress={ handleLongPress }
      onPress={ handlePress }
    >
      { children }
    </TouchableOpacity>
  );
};

@arfa123
Copy link

arfa123 commented Sep 2, 2020

any one have idea how to handle tripple and quadrupple taps like in youtube app?

@yanickrochon
Copy link

@arfa123 have a look at my MultiTapOverlay component, just above your comment; does it work for you? Or do you need a component that will tell you how many taps was pressed (e.g. single, double triple, etc.), handling not only one case but many cases?

@fukemy
Copy link

fukemy commented Aug 12, 2022

@yanickrochon did u read @iksent comment?

@yanickrochon
Copy link

yanickrochon commented Aug 12, 2022

@fukemy I'm not certain what you mean, of course I have read @iksent 's comment. Back in 2020, React hooks were relatively new. So, if I would rewrite the code I posted, it would more look like this one, today :

const MultiPressOverlay: () => ReactNode = ({
  delayLongPress = 1000,
  delayMultiPress = 300,
  minMultiPress = 4,
  onPress,
  onLongPress,
  onMultiPress,
  children
}) => {
  const [lastPress, setLastPress] = useState(null);
  const [pressCount, setPressCount] = useState(0);

  useEffect(() => {
    if ((pressCount > 1) && (pressCount >= minMultiPress)) {
      setPressCount(0);
      onMultiPress && onMultiPress(new CustomEvent("multipress", { detail: { pressCount } }));
    }
  }, [pressCount, minMultiPress, onMultiPress]);

  const handlePress = evt => {
    const now = Date.now();

    setLastPress(now);
    if (now - lastPress <= delayMultiPress) {
       setPressCount(pressCount => pressCount + 1);
    } else {
      setPressCount(1);
    }

    onPress && onPress(evt);
  };

  return (
    <TouchableOpacity
      delayLongPress={ delayLongPress }
      activeOpacity={ 0.8 }
      onLongPress={ onLongPress }
      onPress={ handlePress }
    >
      { children }
    </TouchableOpacity>
  );
};

NOTE: There is still a lot of room for improvement, but this was written without tests whatsoever. I would move the onPress inside the useEffect callback, with a timer, to trigger a single press event is the delayMultiPress is expired and less than minMultiPress has beeen triggered, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment