React Native Prevent Double Tap

后端 未结 10 538
醉梦人生
醉梦人生 2020-12-30 02:22

I have a TouchableHighlight wrapping a Text block that when tapped, opens a new scene (which I\'m using react-native-router-flux).
It\'s all wo

相关标签:
10条回答
  • 2020-12-30 02:40

    After reading several github threads, SO articles and trying most solutions myself I have come to the following conclusions:


    1. Providing an additional key parameter to do "idempotent pushes" does not work consistently as of now. https://github.com/react-navigation/rfcs/issues/16

    2. Using debounce slows down the UX significantly. The navigation only happens X ms after the user has pushed the button the last time. X needs to be large enough to bridge the time where double taps might happen. Which might be anything from 100-600ms really.

    3. Using _.throttle did not work for me. It saved the throttled function call and executed it after the timer ran out resulting in a delayed double tap.


    I considered moving to react-native-navigation but apparently the issue lies deeper and they experience it too.

    So for now I built my own hack that interferes with my code the least:

    const preventDoubleTapHack = (component: any, doFunc: Function) => {
      if (!component.wasClickedYet__ULJyRdAvrHZvRrT7) {
        //  eslint-disable-next-line no-param-reassign
        component.wasClickedYet__ULJyRdAvrHZvRrT7 = true;
        setTimeout(() => {
          //  eslint-disable-next-line no-param-reassign
          component.wasClickedYet__ULJyRdAvrHZvRrT7 = false;
        }, 700);
        doFunc();
      }
    };
    

    anywhere, where we navigate instead of

    this.props.navigation.navigate('MyRoute');
    

    do

    preventDoubleTapHack(this, () => this.props.navigation.navigate('MyRoute');
    

    Beautiful.

    0 讨论(0)
  • 2020-12-30 02:42

    Perhaps you could use the new disable-feature introduced for touchable elements in 0.22? I'm thinking something like this:

    Component

    <TouchableHighlight ref = {component => this._touchable = component}
                        onPress={() => this.yourMethod()}/>
    

    Method

    yourMethod() {
        var touchable = this._touchable;
        touchable.disabled = {true};
    
        //what you actually want your TouchableHighlight to do
    }
    

    I haven't tried it myself. So I'm not sure if it works.

    0 讨论(0)
  • 2020-12-30 02:42

    I fixed this bug by creating a module which calls a function only once in the passed interval.

    Example: If you wish to navigate from Home -> About And you press the About button twice in say 400 ms.

    navigateToAbout = () => dispatch(NavigationActions.navigate({routeName: 'About'}))
    
    const pressHandler = callOnce(navigateToAbout,400);
    <TouchableOpacity onPress={pressHandler}>
     ...
    </TouchableOpacity>
    The module will take care that it calls navigateToAbout only once in 400 ms.
    

    Here is the link to the NPM module: https://www.npmjs.com/package/call-once-in-interval

    0 讨论(0)
  • 2020-12-30 02:43

    What you're trying to do is you want to limit your on tap callbacks, so that they will only run ONCE.

    This is called throttling, and you can use underscore for that: Here's how:

    _.throttle(
        this.thisWillRunOnce.bind(this),
        200, // no new clicks within 200ms time window
    );
    

    Here's how my react component looks after all.

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
         _.throttle(
            this.onPressThrottledCb.bind(this),
            200, // no new clicks within 200ms time window
        );
      }
      onPressThrottledCb() {
        if (this.props.onPress) {
          this.props.onPress(); // this only runs once per 200 milliseconds
        }
      }
      render() {
        return (
          <View>
            <TouchableOpacity onPress={this.onPressThrottledCb}>
            </TouchableOpacity>
          </View>
        )
      }
    }
    

    I hope this helps you. In case you wanna learn more check this thread.

    0 讨论(0)
  • 2020-12-30 02:44

    You could bounce the click at the actual receiver methods, especially if you are dealing with the state for visual effects.

    _onRefresh() {    
        if (this.state.refreshing)
          return
        this.setState({refreshing: true});
    
    0 讨论(0)
  • 2020-12-30 02:51

    I do it like this:

    link(link) {
            if(!this.state.disabled) {
                this.setState({disabled: true});
                // go link operation
                this.setState({disabled: false});
            }
        }
        render() {
            return (
                <TouchableHighlight onPress={() => this.link('linkName')}>
                  <Text>Go link</Text>
                </TouchableHighlight>
            );
        }
    
    0 讨论(0)
提交回复
热议问题