Handling back button in React Native, Navigator on

2020-05-20 07:24发布

问题:

I have a Navigator in an Android react native application.

I'm using navigator.push() to navigate to a different page. It would seem natural that the back button would pop the navigator and to go back one page, but that's not what's happening (it quits the app).

Do the react-native Navigator really has no back button support, do I need to plug it myself with a BackAndroid?

回答1:

Yes, you have to handle the back button yourself. I think the main reason for this is you may want to do different things with the back button instead of just moving back through the stack. I don't know if there are plans to incorporate back button functionality in the future though.



回答2:

In addition to answer above, handling code should be something like this

var navigator; 

React.BackAndroid.addEventListener('hardwareBackPress', () => {
    if (navigator && navigator.getCurrentRoutes().length > 1) {
        navigator.pop();
        return true;
    }
    return false;
});

in render code:

<Navigator ref={(nav) => { navigator = nav; }} />


回答3:

Not sure when the API changed but as of react native 0.31 (potentially earlier versions as well) BackAndroid is a component that must be imported from react-native:

import {..., BackAndroid} from 'react-native'

Also be sure to remove the listener on componentWillUnmount:

componentWillUnmount(){
    BackAndroid.removeEventListener('hardwareBackPress', () => {
        if (this.navigator && this.navigator.getCurrentRoutes().length > 1) {
            this.navigator.pop();
            return true;
        }
        return false;
    });
}

*UPDATE: In react native 0.44 this module has been renamed to BackHandler. Navigator has also been officially deprecated but can still be found here: https://github.com/facebookarchive/react-native-custom-components

import { BackHandler } from 'react-native';


回答4:

Don't forget bind [this]

The correct answer should be:

export default class MyPage extends Component {
  constructor(props) {
    super(props)
    this.navigator = null;

    this.handleBack = (() => {
      if (this.navigator && this.navigator.getCurrentRoutes().length > 1){
        this.navigator.pop();
        return true; //avoid closing the app
      }

      return false; //close the app
    }).bind(this) //don't forget bind this, you will remember anyway.
  }

  componentDidMount() {
    BackAndroid.addEventListener('hardwareBackPress', this.handleBack);
  }

  componentWillUnmount() {
    BackAndroid.removeEventListener('hardwareBackPress', this.handleBack);
  }

  render() {
    return (
      <Navigator
        ref={navigator => {this.navigator = navigator}}
  ...


回答5:

In order to clean the code using my knowledge and previous answers, here is how it should look like:

import { ..., Navigator, BackAndroid } from 'react-native';

componentDidMount() {
  BackAndroid.addEventListener('hardwareBackPress', this.handleBack);
}

componentWillUnmount() {
  //Forgetting to remove the listener will cause pop executes multiple times
  BackAndroid.removeEventListener('hardwareBackPress', this.handleBack);
}

handleBack() {
  if (this.navigator && this.navigator.getCurrentRoutes().length > 1){
    this.navigator.pop();
    return true; //avoid closing the app
  }

  return false; //close the app
}


回答6:

I have created a GitHub repo that will provide you a sample project of how to handle the Android Back Button.

You can clone / download the repo at:

Android Back Button Sample Project

But here are some sample codes on how I handle the android back button

The following code is for my initial screen when user starts my app. Pressing the back button button here would show an alert of which will ask the user whether the user would like to leave the app.

import React, {Component} from 'react';
import {View,Text,Button,BackHandler,Alert} from 'react-native';
import {NavigationActions} from 'react-navigation';

export default class InitScreen extends Component {
    _focusDoneSubscribe;//declaring variable that will be listener for the back button when in focus 
    _blurGoingSubscribe;//declaring variable that will be listener for the back button when blur 

    //the following will remove the navigation bar at the top
    static navigationOptions = {
        header: null,
        title: 'Welcome',
    };

    constructor(props)
    {
        super(props);
        this._focusDoneSubscribe = props.navigation.addListener('didFocus', payload =>
            BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
        );//this assigns the listener
    }
    //functions to handle back button events
    componentDidMount()
    {
        this._blurGoingSubscribe = this.props.navigation.addListener('willBlur', payload =>
                BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
            );
    }

    onBackButtonPressAndroid = () => {
        Alert.alert(
            'Exiting App',
            'Confirm quitting the app?',
            [
                {text: 'CANCEL', style: 'cancel'},
                {text: 'STOP ASKING AND QUIT', onPress: () => BackHandler.exitApp()}
            ],
            {cancelable: false},
        );
        return true;
    };

    componentWillUnmount()
    {
        this._focusDoneSubscribe && this._focusDoneSubscribe.remove();
        this._blurGoingSubscribe && this._blurGoingSubscribe.remove();
    }
    //actual render
    render(){
        const { navigate } = this.props.navigation;
        return (
            <View style={{alignItems: 'center'}}>
                <View style={{height: 100,justifyContent: 'center',alignItems: 'center'}}>
                    <Text style={{fontSize: 20}}>Navigation BackHandler Tutorial</Text>
                    <Text style={{fontSize: 20}}>Initial Screen</Text>
                </View>
                <View style={{flexDirection: 'column', justifyContent: 'space-between', height: 100}}>
                    <Button
                    title="SCREEN ONE"
                    onPress={() => {this.props.navigation.push('One')}}
                    />
                    <Button
                    title="SCREEN TWO"
                    onPress={() => {this.props.navigation.push('Two')}}
                    />
                </View>
            </View>
        );
    }
}

The following code is a screen for which when the user presses the back button, it go back the previous screen:

import React, {Component} from 'react';
import {View,Text,Button,BackHandler} from 'react-native';//declaring variable that will be listener for the back button when in focus 
import {NavigationActions} from 'react-navigation';//declaring variable that will be listener for the back button when blur 

export default class ScreenOne extends Component {
    _focusDoneSubscribe;
    _blurGoingSubscribe;

    //the following will remove the navigation bar at the top
    static navigationOptions = {
        header: null,
        title: 'ONE',
    };

    constructor(props)
    {
        super(props);
        this._focusDoneSubscribe = props.navigation.addListener('didFocus', payload =>
            BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
        );//this assigns the listener
    }
    //functions to handle back button events
    componentDidMount()
    {
        this._blurGoingSubscribe = this.props.navigation.addListener('willBlur', payload =>
                BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
            );
    }

    onBackButtonPressAndroid = () => {
        this.props.navigation.goBack();
        return true;
    };

    componentWillUnmount()
    {
        this._focusDoneSubscribe && this._focusDoneSubscribe.remove();
        this._blurGoingSubscribe && this._blurGoingSubscribe.remove();
    }
    //actual render
    render(){
        const { navigate } = this.props.navigation;
        return (
            <View style={{alignItems: 'center'}}>
                <View style={{height: 100,justifyContent: 'center',alignItems: 'center'}}>
                    <Text style={{fontSize: 20}}>Navigation BackHandler Tutorial</Text>
                    <Text style={{fontSize: 20}}>SCREEN ONE</Text>
                </View>
            </View>
        );
    }
}