Background:
I have a web app built with React (v. 16.4.2 currently). It will only ever be used on a touch screen. It is composed of a ton of buttons to do things, and since it's all touch, I'm using touchstart/touchend to handle these actions.
Example:
This is a basic example of how I'm using the events. You click a button, it sets this.state.exampleRedirect
to true
, which in turn, makes the component re-render and then go to the new page (using react-router-dom). This is all working fine.
<button
type='button'
onTouchStart={() => this.setState({ exampleRedirect: true })}
className='o-button'>
Open modal
</button>
Issue:
I originally used onClick to handle buttons but had issues because my users have fat fingers and not a lot of tech background, and when they'd touch a button, they'd drag their finger over the button and it wouldn't fire the click. OnTouchStart fixes this problem by firing the minute any touch happens (drag, swipe, tap, etc).
The issue is with onTouchStart. A user touches the button, it quickly changes the page (using the router) and re-renders the new page. The app is fast, so this is almost instantaneous, which means that when the new page loads, the user's finger is usually still on the screen, thus firing ANOTHER touch event on whatever they're touching. This is often another routing button, so it just fires through screens until they lift their finger.
I am working around this by putting a delay on enabling buttons on each page load.
// example component
import React, { Component } from 'react';
class ExampleComponent extends Component {
state = { buttonsDisabled: true }
// after 300ms, the buttons are set to enabled (prevents touch events
// from firing when the page first loads
componentWillMount() {
timeoutId = setTimeout(() => {
this.setState({ buttonsDisabled: false });
}, 300);
}
render() {
return (
// button in render method
<button
disabled={this.state.buttonsDisabled}
type='button'
onTouchStart={() => this.setState({ exampleRedirect: true })}
className='o-button'>
Open modal
</button>
);
}
Is there a better way? Or a way to do what I'm doing, but globally so I don't have to add this jankity code in about 100 components?
Thanks!
Instead of using the onTouchStart
event which is fired when a touch point is placed on the touch surface and using a timeout, which is a bit of a hack, you should make use of onTouchEnd
since it will be fired when a touch point is removed from the touch surface, thereby ensuring that the mentioned case doesn't happen.
// example component
import React, { Component } from 'react';
class ExampleComponent extends Component {
state = { buttonsDisabled: true }
// after 300ms, the buttons are set to enabled (prevents touch events
// from firing when the page first loads
componentWillMount() {
timeoutId = setTimeout(() => {
this.setState({ buttonsDisabled: false });
}, 300);
}
render() {
return (
// button in render method
<button
disabled={this.state.buttonsDisabled}
type='button'
onTouchEnd={() => this.setState({ exampleRedirect: true })}
className='o-button'>
Open modal
</button>
);
}