2
votes

I'm building an app with react-navigation-4.2.1. The app has multiple stack navigators. So there are a lots of navigation.push('Routename') calls. Trouble is when the control surface (i.e. TouchableOpacity) is tapped rapidly multiple times (first one, and the rest during screen transition) I end up pushing multiple screens into the stack. Is there a way to restrict the surface to the first tap/call of push()?

1

1 Answers

2
votes

The component below is what i use to make things touchable. it handle multiple touches in small period of time.

  • Use component below instead of TouchableOpacity. wrap any thing you want with this component and it will be touchable.
<SafeTouch
    onPress={...}
>
    <Text> hey! im a touchable text now</Text>
</SafeTouch>
  • The component below is written used TypeScirpt.
  • every touch within 300ms after first touch will be ignored(thats where help you with your problem).
import * as React from 'react'
import { TouchableOpacity } from 'react-native'

interface ISafeTouchProps {
    onPress: () => void
    onLongPress?: () => void
    onPressIn?: () => void
    onPressOut?: () => void,
    activeOpacity?: number,
    disabled?: boolean,
    style: any
}

export class SafeTouch extends React.PureComponent<ISafeTouchProps> {
    public static defaultProps: ISafeTouchProps = {
        onPress: () => { },
        onLongPress: () => { },
        onPressIn: () => { },
        onPressOut: () => { },
        disabled: false,
        style: null
    }
    private isTouchValid: boolean = true
    private touchTimeout: any = null
    public constructor(props: ISafeTouchProps) {
        super(props)
        {// Binding methods
            this.onPressEvent = this.onPressEvent.bind(this)
        }
    }
    public render(): JSX.Element {
        return (
            <TouchableOpacity
                onPress={this.onPressEvent}
                onLongPress={this.props.onLongPress}
                onPressIn={this.props.onPressIn}
                onPressOut={this.props.onPressOut}
                activeOpacity={this.props.activeOpacity}
                disabled={this.props.disabled}
                style={[{minWidth: 24, minHeight: 24}, this.props.style]}
            >
                {
                    this.props.children
                }
            </TouchableOpacity>
        )
    }
    public componentWillUnmount() {
        this.clearTimeoutIfExists()
    }
    private onPressEvent(): void {
        requestAnimationFrame(() => {
            if (this.isTouchValid === false) {
                return
            }
            this.isTouchValid = false
            this.clearTimeoutIfExists()
            this.touchTimeout = setTimeout(() => {
                this.isTouchValid = true
            }, 300)
            if (typeof this.props.onPress === 'function') {
                this.props.onPress()
            }
        })
    }
    private clearTimeoutIfExists(): void {
        if (this.touchTimeout != null) {
            clearTimeout(this.touchTimeout)
            this.touchTimeout = null
        }
    }
}