question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Discussion] dealing with unintentional batched touch events

See original GitHub issue

There is a problem handling quick succession of touches / clicks. This can be categorised as a performance issue.

Use case:

  • user clicks multiple times and quickly on a button
  • events handled in JS by some click handler function which performs a navigation event
  • by the time the navigation event gets completed (some screen is pushed, for example, which hides the button) 2 more clicks was already on their way and this same screen gets pushed multiple times

This is not a react-native specific problem but it gets compounded by the fact all touch events originate in the native’s main thread, dispatched through the bridge asynchronously along with some serialisation mechanism and handled in the JS context (in a different thread).

This is very much like the problem with Android’s startActivity(Intent) which is async, and, if the pushed screen takes too much time to appear, can cause this “batching” of events.

I would appreciate your thoughts on this matter and how do you think it can be handled (with minimum hacks in mind).

Some example solutions:

  • When receiving the event immediately ignore all other events (save something in the state) and enable it back once finished - this is hacky, difficult to maintain, causes the codebase to be a ball of mud, and not alway be possible.
  • add a new prop to all buttons (TouchableHighlight, TouchableOpacity etc) that will be called throttle. When you specify throttle=1000 the button will do something like
_.throttle(onClick, 1000, {leading:true, trailing:false})

which just sends the first event and ignores all other touch events on this specific button in a 1000 millisecond window. See lodash throttle This is also hacky but much simpler to maintain.

Using react-native 0.25.1. Happens on all platforms.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:4
  • Comments:10 (9 by maintainers)

github_iconTop GitHub Comments

7reactions
jdmunrocommented, Jan 5, 2017

If anyone is interested, I’ve used this ‘rough’ solution with some success:

class Touchable extends Component {

  constructor(props) {
    super(props);
    this.onPress = debounce(this.onPress.bind(this), 1000, {
      leading: true,
      trailing: false,
    });
  }

  onPress() {
    if (this.props.onPress) {
      this.props.onPress();
    }
  }

  render() {
    return (
      <TouchableOpacity
        {...this.props}
        onPress={this.props.skipDebounce ? this.props.onPress : this.onPress}
      />
    );
  }
}

It’s not ideal but any means but it solved the most common problems I encountered.

5reactions
talkolcommented, Aug 8, 2016

I want to add a live example showing this issue and why it’s relevant particularly under RN:

https://rnplay.org/apps/cKSvuA

I recommend running the example on your own physical iOS device (using “React Native Playground” from the App Store)

Use case: Show an iOS alert when a button (TouchableOpacity) is pressed Click the button quickly and you’ll see more than one Alert The example here simulates a stress condition in the app (JS thread being busy)

Why this isn’t usually an issue in pure native implementations In pure native code, one would show the alert from the UI thread during the click event handler. Since the alert is displayed synchronously on the UI thread, the user will not be able to tap twice (the UI thread will be blocked until the alert is shown, and when the alert is shown, the button is no longer accessible).

Why is the problem more prominent in RN? Since the entire mechanism is now asynchronous, the touch event needs to be sent over the bridge to JS and then after business logic runs in JS, the resulting change in UI needs to be sent back over the bridge. There are two main potential bottlenecks here:

  • the bridge itself - if the bridge is busy with other data (like other things being sent to native at the same time), there’s a delay until it clears up
  • the JS event loop - if JS is busy with lots of other stuff, there’s a delay as well

These two bottlenecks really do happen in complex apps, making the asynchronicity of the entire interaction feel worst

Where do we suffer from this issue in production? Usually when we have one screen in the app, and the user presses a button that pushes a new screen (using some navigation library). Since the new screen pushed usually incurs a significant render, this makes the interaction sluggish on complex screens. We’ve had jittery users pressing multiple times in succession which results in multiple pushes.

The point of this discussion Obviously every developer can deal with this stuff by himself using some localized band aid. Since the core of the issue is onPress events being sent multiple times when pressed very quickly, it makes sense to add some optional platform-wide mechanism to help deal with this issue. We might even want to add this optional mechanism to all core Touchable components.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using Touch Events - Web APIs | MDN
Browsers typically dispatch emulated mouse and click events when there is only a single active touch point. Multi-touch interactions involving ...
Read more >
Diff - 5747919fed5180956387c50cb03671b9e67be1d5^2 ...
consumeBatchedInputEvents(frameTimeNanos)) { + // If we consumed a batch here, we want to go ahead and schedule the + // consumption of batched...
Read more >
CA2826390A1 - Multimodal touchscreen interaction apparatuses ...
The MULTIMODAL TOUCHSCREEN INTERACTION APPARATUSES, METHODS AND SYSTEMS ("MTI") transform multi-user, multi-modal touchscreen input gestures via MTI ...
Read more >
Search Results for “FILM ” – CHI 2019
Implementing Multi-Touch Gestures with Touch Groups and Cross Events ... and designers may inadvertently perpetuate and instantiate stigma related to mental ...
Read more >
CN101482795B - Mode-based graphical user interfaces for touch ...
For example,, although the present invention mainly for touch-screen, it should be noted in the discussion above that touch pad also can be...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found