React Native - Device back button handling

I want to check if there are more than one screens are on stack when device back button is hit. If yes, I want to show previous screen and if no, I want to exit app.

I have checked number of examples but those use BackAndroid and Navigator. But both of them are deprecated. BackHandler is replacement for BackAndroid. And I can show previous screen by using props.navigation.goBack(null).

But I am unable to find code for finding screen count in stack. I don't want to use deprecated Navigator!


This example will show you back navigation which is expected generally in most of the flows. You will have to add following code to every screen depending on expected behavior. There are 2 cases: 1. If there are more than 1 screen on stack, device back button will show previous screen. 2. If there is only 1 screen on stack, device back button will exit app.

Case 1: Show previous screen

import { BackHandler } from 'react-native';

constructor(props) {
    super(props)
    this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
}

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

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

handleBackButtonClick() {
    this.props.navigation.goBack(null);
    return true;
}

Important: Don't forget to bind method in constructor and to remove listener in componentWillUnmount.

Case 2: Exit App

In this case, no need to handle anything on that screen where you want to exit app.

Important: This should be only screen on stack.


In functional component:

import { BackHandler } from 'react-native';

function handleBackButtonClick() {
    navigation.goBack();
    return true;
  }

  useEffect(() => {
    BackHandler.addEventListener('hardwareBackPress', handleBackButtonClick);
    return () => {
      BackHandler.removeEventListener('hardwareBackPress', handleBackButtonClick);
    };
  }, []);

 import { BackHandler } from 'react-native';
  
 constructor() {
        super();           
        this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
 }

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

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

   handleBackButtonClick() {
       //this.props.navigation.goBack(null);
       BackHandler.exitApp();
       return true;
   }

   handleBackButtonClick() {
       return true;   // when back button don't need to go back 
   }

In Functional Component

import { BackHandler } from 'react-native';

function handleBackButtonClick() {
    navigation.goBack();
    return true;
  }

  useEffect(() => {
    BackHandler.addEventListener('hardwareBackPress', handleBackButtonClick);
    return () => {
      BackHandler.removeEventListener('hardwareBackPress', handleBackButtonClick);
    };
  }, []);

In a case where there are more than one screens stacked in the stack, the default back button behavior in react-native is to navigate back to the previous screen in the stack. Handling the device back button press when having only one screen to exit the app requires a custom setting. Yet this can be achieved without having to add back handling code to each and every screen by modifying the getStateForAction method of the particular StackNavigator's router.

Suppose you have the following StackNavigator used in the application

const ScreenStack = StackNavigator(
  {
    'Screen1': {
      screen: Screen1
    },
    'Screen2': {
      screen: Screen2
    },
  },
  {
    initialRouteName: 'Screen1'
  }
);

The getStateForAction method of the stack navigator's router can be modified as follows to achieve the expected back behavior.

const defaultStackGetStateForAction =
  ScreenStack.router.getStateForAction;

ScreenStack.router.getStateForAction = (action, state) => {
  if(state.index === 0 && action.type === NavigationActions.BACK){
    BackHandler.exitApp();
    return null;
  }

  return defaultStackGetStateForAction(action, state);
};

the state.index becomes 0 only when there is one screen in the stack.