javascript 在 React Native 中防止双击

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47102946/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:12:11  来源:igfitidea点击:

Prevent Double tap in React native

javascriptreact-nativetouchablehighlight

提问by gourav.singhal

How to prevent a user from tapping a button twice in React native?

如何防止用户在 React Native 中点击按钮两次?

i.e. A user must not be able tap twice quickly on a touchable highlight

即用户不能在可触摸的突出显示上快速点击两次

回答by Patrick Wozniak

https://snack.expo.io/@patwoz/withpreventdoubleclick

https://snack.expo.io/@pattwoz/withpreventdoubleclick

Use this HOC to extend the touchable components like TouchableHighlight, Button ...

使用此 HOC 扩展可触摸组件,如 TouchableHighlight、Button ...

import debounce from 'lodash.debounce'; // 4.0.8

const withPreventDoubleClick = (WrappedComponent) => {

  class PreventDoubleClick extends React.PureComponent {

    debouncedOnPress = () => {
      this.props.onPress && this.props.onPress();
    }

    onPress = debounce(this.debouncedOnPress, 300, { leading: true, trailing: false });

    render() {
      return <WrappedComponent {...this.props} onPress={this.onPress} />;
    }
  }

  PreventDoubleClick.displayName = `withPreventDoubleClick(${WrappedComponent.displayName ||WrappedComponent.name})`
  return PreventDoubleClick;
}

Usage

用法

import { Button } from 'react-native';
import withPreventDoubleClick from './withPreventDoubleClick';

const ButtonEx = withPreventDoubleClick(Button);

<ButtonEx onPress={this.onButtonClick} title="Click here" />

回答by Oleksandr Cherniavenko

Use property Button.disabled

使用属性Button.disabled

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, View, Button } from 'react-native';

export default class App extends Component {
  
  state={
    disabled:false,
  }
  
  pressButton() {
    this.setState({
      disabled: true,
    });
    
    // enable after 5 second
    setTimeout(()=>{
       this.setState({
        disabled: false,
      });
    }, 5000)
  }
  
  render() {
    return (
        <Button
            onPress={() => this.pressButton()}
            title="Learn More"
            color="#841584"
            disabled={this.state.disabled}
            accessibilityLabel="Learn more about this purple button"
          />
    );
  }
}



// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => App);

回答by Prateek Surana

If you are using react navigation then use this format to navigate to another page. this.props.navigation.navigate({key:"any",routeName:"YourRoute",params:{param1:value,param2:value}})

如果您使用的是 React 导航,则使用此格式导航到另一个页面。 this.props.navigation.navigate({key:"any",routeName:"YourRoute",params:{param1:value,param2:value}})

The StackNavigator would prevent routes having same keys to be pushed in the stack again. You could write anything unique as the keyand the paramsprop is optional if you want to pass parameters to another screen.

StackNavigator 将阻止具有相同键的路由再次推送到堆栈中。如果您想将参数传递到另一个屏幕,您可以编写任何独特的东西,因为 thekeyparamsprop 是可选的。

回答by Metalliza

I use it by refer the answer above. 'disabled' doesn't have to be a state.

我通过参考上面的答案来使用它。“禁用”不一定是一个状态。

import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';

class PreventDoubleTap extends Component {
    disabled = false;
    onPress = (...args) => {
        if(this.disabled) return;
        this.disabled = true;
        setTimeout(()=>{
            this.disabled = false;
        }, 500);
        this.props.onPress && this.props.onPress(...args);
    }
}

export class ButtonHighLight extends PreventDoubleTap {
    render() {
        return (
            <TouchableHighlight
                {...this.props}
                onPress={this.onPress}
                underlayColor="#f7f7f7"
            />
        );
    }
}

It can be other touchable component like TouchableOpacity.

它可以是其他可触摸组件,如 TouchableOpacity。

回答by Rajesh Nasit

Agree with Accepted answer but very simple way , we can use following way

同意接受的答案但非常简单的方法,我们可以使用以下方法

import debounce from 'lodash/debounce';

    componentDidMount() {

       this.onPressMethod= debounce(this.onPressMethod.bind(this), 500);
  }

onPressMethod=()=> {
    //what you actually want on button press
}

 render() {
    return (
        <Button
            onPress={() => this.onPressMethod()}
            title="Your Button Name"
          />
    );
  }

回答by Littletime

The accepted solution works great, but it makes it mandatory to wrap your whole component to achieve the desired behavior. I wrote a custom React hook that makes it possible to only wrap your callback:

公认的解决方案效果很好,但它必须包装整个组件以实现所需的行为。我写了一个自定义的 React 钩子,它可以只包装你的回调:

useTimeBlockedCallback.js

useTimeBlockedCallback.js

import {?useRef } from 'react'

export default (callback, timeBlocked = 2000) => {
  const isBlockedRef = useRef(false)
  const unblockTimeout = useRef(false)

  return (...callbackArgs) => {
    if (!isBlockedRef.current) {
      callback(...callbackArgs)
    }
    clearTimeout(unblockTimeout.current)
    unblockTimeout.current = setTimeout(() => isBlockedRef.current = false, timeBlocked)
    isBlockedRef.current = true
  }
}

Usage:

用法:

yourComponent.js

你的组件.js

import React from 'react'
import {?View, Text } from 'react-native'
import useTimeBlockedCallback from '../hooks/useTimeBlockedCallback'

export default () => {
  const callbackWithNoArgs = useTimeBlockedCallback(() => {
    console.log('Do stuff here, like opening a new scene for instance.')
  })
  const callbackWithArgs = useTimeBlockedCallback((text) => {
    console.log(text + ' will be logged once every 1000ms tops')
  })

  return (
    <View>
      <Text onPress={callbackWithNoArgs}>Touch me without double tap</Text>
      <Text onPress={() => callbackWithArgs('Hello world')}>Log hello world</Text>
    </View>
  )
}

The callback is blocked for 1000ms after being called by default, but you can change that with the hook's second parameter.

默认情况下,回调在被调用后会被阻止 1000 毫秒,但您可以使用钩子的第二个参数更改它。

回答by André Alencar

I have a very simple solution using runAfterInteractions:

我有一个使用 runAfterInteractions 的非常简单的解决方案:

   _GoCategoria(_categoria,_tipo){

            if (loading === false){
                loading = true;
                this.props.navigation.navigate("Categoria", {categoria: _categoria, tipo: _tipo});
            }
             InteractionManager.runAfterInteractions(() => {
                loading = false;
             });

    };

回答by Peretz30

My implementation of wrapper component.

我的包装器组件的实现。

import React, { useState, useEffect } from 'react';
import { TouchableHighlight } from 'react-native';

export default ButtonOneTap = ({ onPress, disabled, children, ...props }) => {
    const [isDisabled, toggleDisable] = useState(disabled);
    const [timerId, setTimerId] = useState(null);

    useEffect(() => {
        toggleDisable(disabled);
    },[disabled]);

    useEffect(() => {
        return () => {
            toggleDisable(disabled);
            clearTimeout(timerId);
        }
    })


    const handleOnPress = () => {
        toggleDisable(true);
        onPress();
        setTimerId(setTimeout(() => {
            toggleDisable(false)
        }, 1000))
    }
    return (
        <TouchableHighlight onPress={handleOnPress} {...props} disabled={isDisabled} >
            {children}
        </TouchableHighlight>
    )
}

回答by zino

You can also show a loading gif whilst you await some async operation. Just make sure to tag your onPresswith async () => {}so it can be await'd.

您还可以在等待一些异步操作时显示加载 gif。只要确保标记你onPressasync () => {}这样它就可以被await'd。

import React from 'react';
import {View, Button, ActivityIndicator} from 'react-native';

class Btn extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            isLoading: false
        }
    }

    async setIsLoading(isLoading) {
        const p = new Promise((resolve) => {
            this.setState({isLoading}, resolve);
        });
        return p;
    }

    render() {
        const {onPress, ...p} = this.props;

        if (this.state.isLoading) {
            return <View style={{marginTop: 2, marginBottom: 2}}>
                <ActivityIndicator
                    size="large"
                />
            </View>;
        }


        return <Button
            {...p}
            onPress={async () => {
                await this.setIsLoading(true);
                await onPress();
                await this.setIsLoading(false);
            }}
        />
    }

}

export default Btn;