[Discussion] dealing with unintentional batched touch events
See original GitHub issueThere 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 specifythrottle=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:
- Created 7 years ago
- Reactions:4
- Comments:10 (9 by maintainers)
If anyone is interested, I’ve used this ‘rough’ solution with some success:
It’s not ideal but any means but it solved the most common problems I encountered.
I want to add a live example showing this issue and why it’s relevant particularly under RN:
https://rnplay.org/apps/cKSvuA
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:
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.