Реагировать Родной Предотвратить Двойное нажатие

У меня есть TouchableHighlight обертывание Text блокировать, что при нажатии открывает новую сцену (которую я использую реагировать-родной-маршрутизатор-поток).
Все работает нормально, за исключением того, что если вы быстро нажмете на TouchableHighlight, сцена может отрендериться дважды.
Я бы хотел, чтобы пользователь не мог быстро нажать эту кнопку.

Каков наилучший способ сделать это в Native? Я заглянул в систему Gesture Responder System, но там нет примеров или чего-либо в этом роде, что, если вы новичок, как я, сбивает с толку.

12 ответов

То, что вы пытаетесь сделать, - это ограничить количество обратных вызовов, так что они будут работать только ОДИН РАЗ.

Это называется дросселированием, и вы можете использовать underscore для этого:вот как:

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

Вот как выглядит мой компонент реакции.

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>
    )
  }
}

Я надеюсь, это поможет вам. Если вы хотите узнать больше, проверьте эту ветку.

Следующее работает, предотвращая маршрутизацию к одному и тому же маршруту дважды:

import { StackNavigator, NavigationActions } from 'react-navigation';

const App = StackNavigator({
    Home: { screen: HomeScreen },
    Details: { screen: DetailsScreen },
});

// Prevents double taps navigating twice
const navigateOnce = (getStateForAction) => (action, state) => {
    const { type, routeName } = action;
    return (
        state &&
        type === NavigationActions.NAVIGATE &&
        routeName === state.routes[state.routes.length - 1].routeName
    ) ? state : getStateForAction(action, state);
};
App.router.getStateForAction = navigateOnce(App.router.getStateForAction);

Я делаю это так:

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.22? Я думаю что-то вроде этого:

Составная часть

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

метод

yourMethod() {
    var touchable = this._touchable;
    touchable.disabled = {true};

    //what you actually want your TouchableHighlight to do
}

Я сам не пробовал. Так что я не уверен, что это работает.

Я исправил этот метод lodash ниже,

Шаг 1

import { debounce } from 'lodash';

Шаг 2

Поместите этот код в конструктор

this.loginClick = debounce(this.loginClick .bind(this), 1000, {
            leading: true,
            trailing: false,
});

Шаг 3

Напишите на своей кнопке onPress вот так

onPress={() => this.props.skipDebounce ? this.props.loginClick : this.loginClick ()}

Благодарность,

Вы можете отразить щелчок по фактическим методам приемника, особенно если вы имеете дело с состоянием для визуальных эффектов.

_onRefresh() {    
    if (this.state.refreshing)
      return
    this.setState({refreshing: true});

После прочтения нескольких тем GitHub, статей SO и опробования большинства решений я пришел к следующим выводам:


  1. Предоставление дополнительного key Параметр для выполнения "идемпотентных толчков" не работает последовательно на данный момент. https://github.com/react-navigation/rfcs/issues/16

  2. С помощью debounce значительно замедляет UX. Навигация происходит только X мс после того, как пользователь нажал кнопку в последний раз. X должен быть достаточно большим, чтобы преодолеть время, когда могут произойти двойные нажатия. Что может быть что-то от 100-600 мс на самом деле.

  3. С помощью _.throttle не работал для меня. Он сохранил вызов функции удушения и выполнил его после того, как закончился таймер, что привело к задержке двойного нажатия.


Я подумывал переехать в react-native-navigation но, видимо, проблема кроется глубже, и они тоже это испытывают.

Итак, на данный момент я создал свой собственный хак, который меньше всего мешает моему коду:

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();
  }
};

где бы мы ни находились

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

делать

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

Красивая.

Это сработало для меня как обходной путь

      import React from 'react';
import {TouchableOpacity } from 'react-native';

export default SingleClickTouchableOpacity = (props) => {
let buttonClicked = false
return(
    <TouchableOpacity {...props}  onPress={() => {
        if(buttonClicked){
            return
        }
        props.onPress();
        buttonClicked = true 
        setTimeout(() => {
            buttonClicked = false
          }, 1000);
    }}>
        {props.children}
    </TouchableOpacity>
)
}

Если вы используете react-navigationВы можете поставить key собственность на navigate чтобы гарантировать, что в стек будет добавлен только один экземпляр.

через https://github.com/react-navigation/react-navigation/issues/271

Не использовал функцию отключения, setTimeout или не устанавливал лишних вещей.

Таким образом, код выполняется без задержек. Я не избегал двойных нажатий, но я обеспечил выполнение кода только один раз.

Я использовал возвращенный объект из TouchableOpacity, описанный в документации https://reactnative.dev/docs/pressevent, и переменную состояния для управления отметками времени. lastTime - это переменная состояния, инициализированная 0.

      const [lastTime, setLastTime] = useState(0);

...

<TouchableOpacity onPress={async (obj) =>{
    try{
        console.log('Last time: ', obj.nativeEvent.timestamp);
        if ((obj.nativeEvent.timestamp-lastTime)>1500){  
            console.log('First time: ',obj.nativeEvent.timestamp);
            setLastTime(obj.nativeEvent.timestamp);

            //your code
            SplashScreen.show();
            await dispatch(getDetails(item.device));
            await dispatch(getTravels(item.device));
            navigation.navigate("Tab");
            //end of code
        }
        else{
            return;
        }
    }catch(e){
        console.log(e);
    }           
}}>

Я использую асинхронную функцию для обработки отправлений, которые на самом деле извлекают данные, в конце концов я в основном перехожу на другой экран.

Я распечатываю первый и последний раз между касаниями. Я выбираю, чтобы между ними существовала разница не менее 1500 мс, и избегаю двойного нажатия паразитов.

Вы можете использовать debounce очень простым способом

import debounce from 'lodash/debounce';

componentDidMount() {

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

 yourMethod=()=> {
    //what you actually want your TouchableHighlight to do
}

<TouchableHighlight  onPress={this.yourMethod}>
 ...
</TouchableHighlight >

Я исправил эту ошибку, создав модуль, который вызывает функцию только один раз за прошедший интервал.

Пример: если вы хотите перейти от Home -> About, и вы дважды нажимаете кнопку About, скажем, 400 мс.

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.

Вот ссылка на модуль NPM: https://www.npmjs.com/package/call-once-in-interval

Другие вопросы по тегам