React-native-router-flux: App not exit when press back button on Android

Created on 10 Oct 2017  路  10Comments  路  Source: aksonov/react-native-router-flux

Version

  • react-native-router-flux v4.0.0-beta.21
  • react-native v0.49.0

Expected behaviour

  • Exit app when press back button.

This is my router.

<Router>
      <Scene key="root" hideNavBar>
        <Scene key="start" hideNavBar>
          <Scene key="splash" component={Splash} initial />
          <Scene key="startapp" component={AppStart} />
        </Scene>
        <Scene key="Drawer" drawer hideNavBar contentComponent={SideBar}>
          <Scene key="main" hideNavBar>
            <Scene key="home" component={Home} title="Main" initial />
            <Scene key="payment" component={Payment} title="Pricing" />
            <Scene key="vacuum" component={Vacuum} title="Vacuum" />
            <Scene key="iron" component={Iron} title="Iron" />
            <Scene key="dry" component={Dry} title="Dry" />
            <Scene key="history" component={History} title="History" />
            <Scene key="settings" component={Settings} title="Setiing" />
            <Scene key="notipay" component={Notipay} title="Notipay" />
            <Scene key="usernoti" component={UserNoti} title="UserNoti" />
          </Scene>
        </Scene>
        <Scene key='EmpDrawer' drawer hideNavBar contentComponent={EmpSideBar}>
          <Scene key="EmpMain" hideNavBar>
            <Scene key="empoyee" component={Employee} title="Employee" initial />
            <Scene key="settings" component={Settings} title="Setiing" />
          </Scene>
        </Scene>
        <Scene key="auth" hideNavBar>
          <Scene key="welcome" component={Welcome} title="Welcome To WashClean" initial />
          <Scene key="login" component={Login} title="Please Login" />
          <Scene key="register" component={Register} title="Register" />
          <Scene key="privacy" component={Privacy} title="Privacy" />
          <Scene key="terms" component={Terms} title="Terms" />
        </Scene>
      </Scene>
    </Router>

Go to Drawer or auth :

Actions.Drawer({type: ActionConst.RESET});
Actions.auth({type: ActionConst.RESET});

Handle back button :

componentDidMount () {
    BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
  }

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

  onBackPress () {
    if (Actions.state.index === 0) {
      return false;
    }

    Actions.pop();
    return true;
  }

Actual behaviour

onBackPress return false but my app nothing happen.

Most helpful comment

as @vishnuc said, add this in your Router code :

  onBackPress() {
    if (Actions.state.index === 0) {
      return false
    }
    Actions.pop()
    return true
  }
...
...
  <Router backAndroidHandler={this.onBackPress}>

Then, the backButton will be handled by the library and if you want to do a specific action in a component with the backButton, it will override the onBackPress function.

Like this :

import {  BackHandler} from 'react-native'
...
...
  componentWill/DidMount() {
      this.homeBackPressHandler = BackHandler.addEventListener('homeBackPress', () => {
        if (Actions.currentScene === 'home' && myCondition) {
          doStuff()
          return true
        }
        return false
      })
  }

  componentWillUnmount() {
    this.homeBackPressHandler.remove()
  }

Doing that in my Android app, never had a problem.

All 10 comments

same thing happens to me

+1

I found solution, just use BackHandler.exitApp() instead of return false
My code :

constructor(props) {
    super(props);
    this.state = {
      doubleBackToExitPressedOnce: false
    }
  }

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

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

  onButtonPress = () => {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    // then navigate
    // navigate('NewScreen');
  }

  handleBackButton = () => {
    if(this.state.doubleBackToExitPressedOnce) {
      BackHandler.exitApp();
    }
    ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
    this.setState({ doubleBackToExitPressedOnce: true });
    setTimeout(() => {
      this.setState({ doubleBackToExitPressedOnce: false });
    }, 2000);
    return true;
  }

Actual solution is to use backAndroidHandler in <Router> and return false only in home scene to exit.. you can get current scene by Actions.currentScene

As a workaround, you can get a hold of the action and test for 'Navigation/BACK' in the reducer. Then you can call BackHandler.exitApp(). Obviously, this solution is sub optimal but it does work if you're stuck as the listener doesn't fire but the events are still coming through the reducer.
Here is the reducer from the sample project amended for this purpose:

const reducerCreate = params => {
  const defaultReducer = new Reducer(params);
  return (state, action) => {
    if(action.type === 'Navigation/BACK' && state.index === 0){
        BackHandler.exitApp()
    }
    console.log('ACTION:', action);
    return defaultReducer(state, action);
  };
};

Officially, I don't recommend this but I didn't find any other way to make it happen.

as @vishnuc said, add this in your Router code :

  onBackPress() {
    if (Actions.state.index === 0) {
      return false
    }
    Actions.pop()
    return true
  }
...
...
  <Router backAndroidHandler={this.onBackPress}>

Then, the backButton will be handled by the library and if you want to do a specific action in a component with the backButton, it will override the onBackPress function.

Like this :

import {  BackHandler} from 'react-native'
...
...
  componentWill/DidMount() {
      this.homeBackPressHandler = BackHandler.addEventListener('homeBackPress', () => {
        if (Actions.currentScene === 'home' && myCondition) {
          doStuff()
          return true
        }
        return false
      })
  }

  componentWillUnmount() {
    this.homeBackPressHandler.remove()
  }

Doing that in my Android app, never had a problem.

I'm having an issue with exiting the app when navigation from other screens work on initial load though.
https://stackoverflow.com/questions/47898965/exist-app-when-clicked-back-on-home-screen

I found solution, just use BackHandler.exitApp() instead of return false
My code :

constructor(props) {
    super(props);
    this.state = {
      doubleBackToExitPressedOnce: false
    }
  }

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

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

  onButtonPress = () => {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    // then navigate
    // navigate('NewScreen');
  }

  handleBackButton = () => {
    if(this.state.doubleBackToExitPressedOnce) {
      BackHandler.exitApp();
    }
    ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
    this.setState({ doubleBackToExitPressedOnce: true });
    setTimeout(() => {
      this.setState({ doubleBackToExitPressedOnce: false });
    }, 2000);
    return true;
  }

@dreamteryst how can this be made to work when there's an app drawer? My challenge is that subsequent screens continue to see Home screen, as this.props.navigation.state.routeName === 'Home'.

@dreamteryst Blapi

as @vishnuc said, add this in your Router code :

  onBackPress() {
    if (Actions.state.index === 0) {
      return false
    }
    Actions.pop()
    return true
  }
...
...
  <Router backAndroidHandler={this.onBackPress}>

Then, the backButton will be handled by the library and if you want to do a specific action in a component with the backButton, it will override the onBackPress function.

Like this :

import {  BackHandler} from 'react-native'
...
...
  componentWill/DidMount() {
      this.homeBackPressHandler = BackHandler.addEventListener('homeBackPress', () => {
        if (Actions.currentScene === 'home' && myCondition) {
          doStuff()
          return true
        }
        return false
      })
  }

  componentWillUnmount() {
    this.homeBackPressHandler.remove()
  }

Doing that in my Android app, never had a problem.

aaaa. thank you! I half a day could not find a solution!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YouYII picture YouYII  路  3Comments

sreejithr picture sreejithr  路  3Comments

vinayr picture vinayr  路  3Comments

basdvries picture basdvries  路  3Comments

willmcclellan picture willmcclellan  路  3Comments