React native: дважды нажмите назад, чтобы выйти из приложения

Как выйти из приложения, дважды нажав кнопку возврата, без необходимости использования Redux

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

Ch Soal khubi ... Damet garm javab awli vase appam gereftam

MoHammaD ReZa DehGhani 14.02.2019 13:21

ташакор барадар азизам @MoHammaDReZaDehGhani

Mahdi Bashirpour 16.02.2019 08:16
11
2
17 246
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий
import React, {Component} from 'react';
import {BackHandler, View, Dimensions, Animated, TouchableOpacity, Text} from 'react-native';

let {width, height} = Dimensions.get('window');


export default class App extends Component<Props> {

    state = {
        backClickCount: 0
    };
    
    constructor(props) {
        super(props);

        this.springValue = new Animated.Value(100) ;

    }

    componentWillMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    _spring() {
        this.setState({backClickCount: 1}, () => {
            Animated.sequence([
                Animated.spring(
                    this.springValue,
                    {
                        toValue: -.15 * height,
                        friction: 5,
                        duration: 300,
                        useNativeDriver: true,
                    }
                ),
                Animated.timing(
                    this.springValue,
                    {
                        toValue: 100,
                        duration: 300,
                        useNativeDriver: true,
                    }
                ),

            ]).start(() => {
                this.setState({backClickCount: 0});
            });
        });

    }


    handleBackButton = () => {
        this.state.backClickCount == 1 ? BackHandler.exitApp() : this._spring();

        return true;
    };


    render() {

        return (
            <View style={styles.container}>
                <Text>
                    container box
                </Text>

                <Animated.View style={[styles.animatedView, {transform: [{translateY: this.springValue}]}]}>
                    <Text style={styles.exitTitleText}>press back again to exit the app</Text>

                    <TouchableOpacity
                        activeOpacity={0.9}
                        onPress={() => BackHandler.exitApp()}
                    >
                        <Text style={styles.exitText}>Exit</Text>
                    </TouchableOpacity>

                </Animated.View>
            </View>
        );
    }
}


const styles = {
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    animatedView: {
        width,
        backgroundColor: "#0a5386",
        elevation: 2,
        position: "absolute",
        bottom: 0,
        padding: 10,
        justifyContent: "center",
        alignItems: "center",
        flexDirection: "row",
    },
    exitTitleText: {
        textAlign: "center",
        color: "#ffffff",
        marginRight: 10,
    },
    exitText: {
        color: "#e5933a",
        paddingHorizontal: 10,
        paddingVertical: 3
    }
};

Беги в snake.expo: https://snack.expo.io/HyhD657d7

Вы можете это проверить, пожалуйста, мистер Махди! stackoverflow.com/questions/55306718/…

DevAS 22.03.2019 22:32

Не думаю, что нам нужны такие сложные примеры. Ознакомьтесь с моим ответом.

manish kumar 28.03.2020 18:57

@manishkumar Благодарен. Этот ответ был получен полтора года назад, и его необходимо пересмотреть. Я тоже предлагал тебе уступку 😊

Mahdi Bashirpour 28.03.2020 21:40

вы можете обновить ответ на основе последних изменений. Поскольку вопрос не в версии. или укажите версию. Было бы полезно пользователям.

manish kumar 29.03.2020 08:30

Добавлены UNSAFE_componentWillMount и UNSAFE_componentWillUnmount, которые теперь работают без предупреждений

subramanya46 23.06.2020 09:08

Ниже код объясняет сам себя. Уловка состоит в том, чтобы иметь его в Главный AppContainer, а не в каждая страница

import {  Alert,  BackHandler,  ToastAndroid } from 'react-native';
import {  StackActions } from 'react-navigation';
// common statless class variable.
let backHandlerClickCount = 0;

class App extends React.Component {
    constructor(props) {
      super(props);
      // add listener to didFocus
      this._didFocusSubscription = props.navigation.addListener('didFocus', payload =>
        BackHandler.addEventListener('hardwareBackPress', () => this.onBackButtonPressAndroid(payload)));
    }

    // remove listener on unmount 
    componentWillUnmount() {
      if (this._didFocusSubscription) {
        this._didFocusSubscription.remove();
      }
    }

    onBackButtonPressAndroid = () => {
      const shortToast = message => {
        ToastAndroid.showWithGravityAndOffset(
          message,
          ToastAndroid.SHORT,
          ToastAndroid.BOTTOM,
          25,
          50
        );

        const {
          clickedPosition
        } = this.state;
        backHandlerClickCount += 1;
        if ((clickedPosition !== 1)) {
          if ((backHandlerClickCount < 2)) {
            shortToast('Press again to quit the application!');
          } else {
            BackHandler.exitApp();
          }
        }

        // timeout for fade and exit
        setTimeout(() => {
          backHandlerClickCount = 0;
        }, 2000);

        if (((clickedPosition === 1) &&
            (this.props.navigation.isFocused()))) {
          Alert.alert(
            'Exit Application',
            'Do you want to quit application?', [{
              text: 'Cancel',
              onPress: () => console.log('Cancel Pressed'),
              style: 'cancel'
            }, {
              text: 'OK',
              onPress: () => BackHandler.exitApp()
            }], {
              cancelable: false
            }
          );
        } else {
          this.props.navigation.dispatch(StackActions.pop({
            n: 1
          }));
        }
        return true;
      }

    }

Вы можете проверить это, пожалуйста !! stackoverflow.com/questions/55306718/…

DevAS 22.03.2019 22:33
import React, { Component } from 'react'
import { Text, View, StyleSheet, TouchableOpacity,BackHandler} from 'react-native'
import { Toast } from "native-base";

class Dashboard extends Component {

    state={
        clickcount:0
    }


    componentDidMount(){
        BackHandler.addEventListener("hardwareBackPress",()=>{
            this.setState({'clickcount':this.state.clickcount+1})
            this.check();
            return true
        })
    }

    check=()=>{
        if(this.state.clickcount<2){
            Toast.show({
                text:`Press back again to exit App `,
                duration:2000,
                onClose:()=>{this.setState({'clickcount':0})}
            })

        }
        else if(this.state.clickcount==2)
        {
            BackHandler.exitApp()
        }
    }

    render() {
        return (

                <View style={styles.container}>
                    <Text> Hello this is the Dashboard Screen</Text>
                </View>

        )
    }
}

export default Dashboard

const styles = StyleSheet.create({
 container:{
     flex:1,
     marginTop:25,
     justifyContent:'center',
     alignItems:'center',
     borderBottomLeftRadius:30,
     borderBottomRightRadius:30,
     backgroundColor:'#fff'
    },
  });

Добро пожаловать в SO! Пожалуйста, подумайте о форматировании вашего кода. Спасибо!

joe 13.02.2020 17:18

Вместо этого используйте ToastAndroid из библиотеки реагировать-родной. Посмотри мой ответ

manish kumar 28.03.2020 18:58

Лучшим подходом было бы просто использовать BackHandler и ToastAndroid.

import { BackHandler, ToastAndroid } from 'react-native';
//rest of imports

class SomeClass extends Component{
    constructor(state, props) {
        super(state, props)
        this.state = {
            validCloseWindow: false
        }
    }
    async componentDidMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }
    handleBackButton = () => {
        if (!this.props.navigation.canGoBack()) {
            if (this.state.validCloseWindow)
                return false;
            this.state.validCloseWindow = true
            setTimeout(() => {
                this.state.validCloseWindow = false
            }, 3000);
            ToastAndroid.show("Press Again To Exit !", ToastAndroid.SHORT);
            return true;
        }
    };
//rest of component code
}

Просто убедитесь, что используете его на странице initialRoute для навигации.

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

import * as React from 'react';
import {useEffect, useState} from 'react';
import {Platform, BackHandler, ToastAndroid} from 'react-native';

export const ExecuteOnlyOnAndroid = (props) => {
  const {message} = props;
  const [exitApp, setExitApp] = useState(0);
  const backAction = () => {
    setTimeout(() => {
      setExitApp(0);
    }, 2000); // 2 seconds to tap second-time

    if (exitApp === 0) {
      setExitApp(exitApp + 1);

      ToastAndroid.show(message, ToastAndroid.SHORT);
    } else if (exitApp === 1) {
      BackHandler.exitApp();
    }
    return true;
  };
  useEffect(() => {
    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      backAction,
    );
    return () => backHandler.remove();
  });
  return <></>;
};

export default function DoubleTapToClose(props) {
  const {message = 'tap back again to exit the App'} = props;
  return Platform.OS !== 'ios' ? (
    <ExecuteOnlyOnAndroid message={message} />
  ) : (
    <></>
  );
}

=> Единственное, что вам нужно, это включить этот компонент в свое приложение.. <=

Поскольку IOS не имеет кнопки возврата, IOS не нуждается в этой функции. Вышеупомянутый компонент автоматически определяет, является ли устройство Android или нет.

По умолчанию сообщение, отображаемое в Toast, предопределено на английском языке, но вы можете установить свое собственное, если добавите свойство с именем message в свой DoubleTapToClose-Component.

...
import DoubleTapToClose from '../lib/android_doubleTapToClose'; 
...
return(
    <>
        <DoubleTapToClose />
        ...other Stuff goes here
    </>

В зависимости от вашей навигационной структуры вы должны проверить, находитесь ли вы на начальном экране своего приложения или нет. В моем случае у меня есть ящик с несколькими StackNavigatiors внутри. Поэтому я проверяю, является ли текущий экран начальным экраном (индекс: 0) или нет. Если это так, я устанавливаю Hook-Variable, которая будет использоваться только для этих начальных экранов.

Выглядит так:

const isCurrentScreenInitialOne = (state) => {
  const route = state.routes[state.index];
  if (route.state) {
    // Dive into nested navigators
    return isCurrentScreenInitialOne(route.state);
  }
  return state.index === 0;
};

...
...

export default function App() {
...
  const [isInitialScreen, setIsInitialScreen] = useState(true);

  {isInitialScreen && (<DoubleTapToClose message="Tap again to exit app" />)}

...
...
<NavigationContainer
          ...
          onStateChange={(state) => {
            setIsInitialScreen(isCurrentScreenInitialOne(state));
          }}>

Если это описание вам поможет, не пропустите голосование.

Какой умный подход, @suther!

LordKiz 18.05.2020 15:54

Вместо всех проверок, разве canGoBack просто не сообщает вам, собираетесь ли вы выйти из приложения?

Exis Zhang 14.07.2020 02:38

@ exis-zhang: В canGoBack нет функции React-Navigation. Похоже, вы перепутали reactJS (и Browser-API ... вот и canGoBack) и react-native / react-navigation.

suther 14.07.2020 12:28

самый простой подход на данный момент:

в App.js :

componentDidMount() {
    const backHandler=BackHandler.addEventListener('hardwareBackPress', ()=>{
        if(this.backHandler){
            return false;
        }
        Toast.show('再按一次退出应用');
        this.backHandler=backHandler;
        setTimeout(()=>{
            this.backHandler=null;
        },2000);
        return true;
    });
}

componentWillUnmount() {
    this.backHandler.remove();
}

Извините, если я опаздываю на вечеринку, но у меня было аналогичное требование, и я решил его, создав свой собственный крючок!

let currentCount = 0;
export const useDoubleBackPressExit = (exitHandler: () => void) => {
  if (Platform.OS === "ios") return;
  const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
    if (currentCount === 1) {
      exitHandler();
      subscription.remove();
      return true;
    }
    backPressHandler();
    return true;
  });
};
const backPressHandler = () => {
  if (currentCount < 1) {
    currentCount += 1;
    WToast.show({
      data: "Press again to close!",
      duration: WToast.duration.SHORT,
    });
  }
  setTimeout(() => {
    currentCount = 0;
  }, 2000);
};

Теперь я могу использовать его где угодно, просто выполнив:

useDoubleBackPressExit(() => { 
   // user has pressed "back" twice. Do whatever you want! 
});

Самое простое решение, которое я использовал в своем приложении, - это. Он отлично работает с react-navigation 4.4.1 и намного короче, чем некоторые другие правильные ответы, представленные здесь.

import React from 'react';
import { BackHandler, ToastAndroid} from 'react-native';

export default class LoginScreen extends React.Component {

    state = {
        canBeClosed: false
    }

    componentDidMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
    }
      
    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    }

    handleBackButton = () => {
        if (this.props.navigation.isFocused()) {
            if (this.state.canBeClosed)
                return this.state.canBeClosed = false;
    
            else {
                setTimeout(() => { this.state.canBeClosed = false }, 3000);    
                ToastAndroid.show("Press Again To Exit !", ToastAndroid.SHORT);
    
                return this.state.canBeClosed = true
            }   
        }
    };

 //some code

}

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