Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Hardware Back Button not Working #1603

Closed
StevePotter opened this issue May 19, 2017 · 32 comments
Closed

Android Hardware Back Button not Working #1603

StevePotter opened this issue May 19, 2017 · 32 comments

Comments

@StevePotter
Copy link

StevePotter commented May 19, 2017

Great work on this package. Pressing the back button in the nav header works, but if I press the back button on the device, nothing happens.

I looked in the source and found 'backPress' handled from BackAndroid: https://github.com/react-community/react-navigation/blob/master/src/createNavigationContainer.js#L157

There are two problems here. First, the hardwareBackPress needs to be handled and it should return true/false to prevent the app from exiting if indeed the user can navigate backwards.

For now I've written this functionality into my app but since some work has been put in to handle Android, I would expect it to work for the phone's physical back button. I'd be happy to submit a PR if it will be taken seriously.

Thanks for your work and let me know if I can help.

@rigobcastro
Copy link

What is your RN version? We need a little more info for help you

@luhui
Copy link

luhui commented May 22, 2017

same problem, my RN version is 0.44.0

@rigobcastro
Copy link

@luhui check this #1330 could help

@Suresh-R-S
Copy link

Any workaround.? I've been struggling to handle android hardware back button.

@rigobcastro
Copy link

@Suresh-R-S Have you read this https://facebook.github.io/react-native/docs/backandroid.html? Maybe this is your problem

@Suresh-R-S
Copy link

Suresh-R-S commented Jun 10, 2017

@rigobcastro Yes i'am using backHandler, since backAndroid is deprecated from RN 0.44.0.
I've added event listeners to all the screens. But I'am having trouble with removing the listener. The componentWillUnmount is not fired.
Eg. If i have 2 screens, Scr 1 & Scr 2.
Since the Scr 1 listener is not removed, pressing on hardware button from Scr 2 invokes the listener of Scr 1.

@rigobcastro
Copy link

@Suresh-R-S ok this is another problem, componentWillUnmount doesn't work and I don't know why (I have the same problem with tabs) but maybe componentDidMount works or try the use the listener in App.js I don't know, I think many listener for one event is not a good practice the good practice will be one listener and many callbacks in all components/screens/containers. It's my opinion.

@spencercarli
Copy link
Member

This is working in the most recent release! 😄

@rahmatkruniawan
Copy link

@spencercarli which version ??

@samdoj
Copy link

samdoj commented Sep 29, 2017

What other packages are you using? In my case there was some weird conflict with react-native-video using react-windows. When I upgraded my react-native-video, BackHandler worked fine.

@rahmatkruniawan
Copy link

@samdoj in dev mode work but on release mode back not working

@vikas199
Copy link

how to determine the hardware buttons in android devices are present or not in react native as there seem to be a lot of issues in styling various android devices please anyone could help me out in resolving that isuue

@austenLacy
Copy link

@rahmatkruniawan

I am on v0.46.0 of react-native and had the same issue. I tracked the issue down to this file in the react-native code base

https://github.com/facebook/react-native/blob/master/Libraries/Utilities/BackHandler.android.js#L25

When running with the chrome debugger turned off the line

var subscriptions = Array.from(_backPressSubscriptions.values()).reverse()

always returns an empty array for subscriptions which in turn causes the invokeDefault variable to stay true and the .exitApp() function to be called.

After more investigation, I think the issue was discovered and discussed in the following PR facebook/react-native#15182.

Even after copy/pasting the PR change in an older version of RN it did not work most likely caused by the issue described in the PR.

After some very slight modifications I got it working by changing to

RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
  var invokeDefault = true;
  var subscriptions = []
  _backPressSubscriptions.forEach(sub => subscriptions.push(sub))

  for (var i = 0; i < subscriptions.reverse().length; ++i) {
    if (subscriptions[i]()) {
      invokeDefault = false;
      break;
    }
  }

  if (invokeDefault) {
    BackHandler.exitApp();
  }
});

Simply using a .forEach which was the original implementation on the PR before the amended Array.from syntax works throughout.

So you could fork react-native and use a modified version, submit a PR though I imagine that will take a little while to be approved and merged upstream, or you can do something similar to what I did which was to override the RCTDeviceEventEmitter.addListener(...) for the hardwareBackPress event.

// other imports
import { BackHandler, DeviceEventEmitter } from 'react-native'

class MyApp extends Component {
  constructor(props) {
    super(props)
    this.backPressSubscriptions = new Set()
  }

  componentDidMount = () => {
    DeviceEventEmitter.removeAllListeners('hardwareBackPress')
    DeviceEventEmitter.addListener('hardwareBackPress', () => {
      let invokeDefault = true
      const subscriptions = []

      this.backPressSubscriptions.forEach(sub => subscriptions.push(sub))

      for (let i = 0; i < subscriptions.reverse().length; i += 1) {
        if (subscriptions[i]()) {
          invokeDefault = false
          break
        }
      }

      if (invokeDefault) {
        BackHandler.exitApp()
      }
    })

    this.backPressSubscriptions.add(this.handleHardwareBack)
  }

  componentWillUnmount = () => {
    DeviceEventEmitter.removeAllListeners('hardwareBackPress')
    this.backPressSubscriptions.clear()
  }

  handleHardwareBack = () => { /* do your thing */ }
  
  render() { return <YourApp /> }
}

@haozes
Copy link

haozes commented Jan 4, 2018

@austenLacy I use this code ,still not work. 😂

@austenLacy
Copy link

austenLacy commented Jan 4, 2018

@haozes if you post the code that you have, I can try to help if you'd like

@petarivanovv9
Copy link

@austenLacy I use this code, but it's still not working.
I'm on

react-native: 0.52.0,
react-navigation: 1.0.0-beta.23

Here's some peace of my code in one js file:

class CustomDrawerView extends Component {
  constructor(props) {
    super(props);
    this.backPressSubscriptions = new Set();
  }

  componentDidMount = () => {
    DeviceEventEmitter.removeAllListeners('hardwareBackPress')
    DeviceEventEmitter.addListener('hardwareBackPress', () => {
      let invokeDefault = true;
      const subscriptions = [];

      this.backPressSubscriptions.forEach(sub => subscriptions.push(sub));

      for (let i = 0; i < subscriptions.reverse().length; i += 1) {
        if (subscriptions[i]()) {
          invokeDefault = false;
          break;
        }
      }

      if (invokeDefault) {
        BackHandler.exitApp();
      }
    });

    this.backPressSubscriptions.add(this.handleHardwareBack);
  }

  componentWillUnmount() {
    console.log('CustomDrawerView .. componentWillUnmount ..');
  //   if (Platform.OS === 'android') BackHandler.removeEventListener('hardwareBackPress');
    DeviceEventEmitter.removeAllListeners('hardwareBackPress');
    this.backPressSubscriptions.clear();
  }

  handleHardwareBack = () => {
    const { navigation } = this.props;
    if (navigation.state.routes.length === 1 &&
        (navigation.state.routes[0].routeName === 'StartUpScreen' ||
          navigation.state.routes[0].routeName === 'SignInScreen' ||
          navigation.state.routes[0].routeName === 'ListServicesScreen'
        )
      ) {
      return false;
    }

    navigation.dispatch(NavigationActions.back());
    return true;
  }

// some other logic for the Custom Drawer component
}

const CustomDrawer = createNavigationContainer(
  createNavigator(AppRouter)(CustomDrawerView)
);

class App extends Component {
  constructor(props) {
    super(props);
  }

  componentWillMount() {
    console.log('App .. componentWillMount ..');

    // configure firebase database
    firebase.initializeApp(firebaseConfig);
  }

  render() {
    const store = configureStore();

    store.dispatch(verifyIsLoggedIn());

    return (
      <Provider store={store}>
        <CustomDrawer />
      </Provider>
    );
  }
}

export default App;

I will be very happy if someone could help me with this BackButton on Android. I use console.log(...) in BackHandler listener but it's not fired and I don't know why. :(
Thanks!

@austenLacy
Copy link

@Pepincho is your CustomDrawerView component your root level app component? It needs to be in the root component because you don't want the app to unmount which would remove your custom listener.

@mhdanh
Copy link

mhdanh commented Jun 10, 2018

@Pepincho I dont know whether this is actually a problem or not but when i comment firebase backhandler working for me.
My current firebase version is "firebase": "^5.0.4", => for the lastest i havent try it yet
when i downgrade to "firebase": "^4.13.1", it working perfectly with BackHandler

@alz10
Copy link

alz10 commented Jun 12, 2018

@Pepincho Hello, im having the same problem with back button. My problem is when i press the Device Back Button it Forces the app to close. I tried to create a new project and test each and every plugin i installed.

Device Back Button seems to be working fine to every plugin except when i import firebase from 'firebase' the Device Back Button never works. If you have fix to this, please share it

@alz10
Copy link

alz10 commented Jun 12, 2018

@maidanhcongtu DUUUUUUUUUUUUDE!!!!!!! You saved me. It worked now. I had to downgrade firebase to npm install firebase@4.13.1 and the Device Back Button works for me now even without using BackHandler

@brentvatne
Copy link
Member

can someone here investigate what changed between firebase 4.13.1 and 5.0.4 that may somehow cause this? there must be some weird side effect of importing the lib that is causing this issue

@mhdanh
Copy link

mhdanh commented Jun 13, 2018

Just information that when I try firebase version 5.0.3 it's also working for me

@rlaw23
Copy link

rlaw23 commented Jul 13, 2018

Downgraded Firebase from 5.0.4 to 5.0.3, works for me. Didn't expect an UI problem was related to Firebase, cost me 2 days...

@brentvatne
Copy link
Member

brentvatne commented Jul 13, 2018

I have no idea what firebase is doing here :\ If someone who uses firebase and react-navigation could dig in and let me know what you find, it would be much appreciated

@rasanu
Copy link

rasanu commented Jul 14, 2018

@maidanhcongtu thnx a lot you saved me...

@getenlever123
Copy link

Wow, I had also issues. Thank's @maidanhcongtu

@SergeyIntelweb
Copy link

@austenLacy I spent 3 days and so I did not this, You save my nerves. Thanks

@waiholiu
Copy link

oh my god, I tried so many things. I thought it was expo, I thought maybe it was my phone, maybe it was the way I was doing my routing in React Navigation. I created hello world solutions trying to isolate exactly why this was happening.

And of all things, it was firebase...

@RishavKumar-3796
Copy link

Guyz please do understand it might not only be the problem with react native. Be careful while integrating it with firebase.
The recent firebase version has the problem of integrating back button in react-native apps!!
Please downgrade the firebase version to firebase-version @5.0.3 and then recheck whether it works or not!
I had the same issue and was worried for days. I finally downgraded to @5.0.3 version and now the back button works perfectly fine!
You may downgrade to lower versions if still facing the problem.

@southerneer
Copy link

This is confounding. I can also repro with react-navigation + latest Firebase (5.5.2 as of writing). If I downgrade to firebase@5.0.3 I see the react-navigation handler firing and everything runs as expected. And if I bump it up to firebase@5.0.4 it fails.

There isn't anything obviously intrusive about firebase's code. The release notes don't raise any red flags. And here is the diff between 5.0.3 and 5.0.4. Nothing jumps off the page there either...

@Dric0
Copy link

Dric0 commented Oct 23, 2018

Firebase 5.5.5 still has the issue

@brentvatne
Copy link
Member

let them know on their issues @Dric0

@react-navigation react-navigation locked and limited conversation to collaborators Oct 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests