Upon putting the Android app into the background by using the hardware back button and then re-opening the app, the entire app gets re-initialised and all the root components code gets executed again (constructor, componentDidMount). This causes things like event listeners to stack up.
What I expect to happen when pressing the back button is for the app to just go into the background, just like pressing the home button on iOS. When you open the app back up, nothing should re-initialise.
index.android.js/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
let count = 0;
export default class TestApp extends Component {
componentDidMount() {
count++;
console.log('mounted', count);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('TestApp', () => TestApp);
Stop the app re-mounting when the Android app is closed from the hardware back button or explain if this is expect behaviour and why it's different from pressing the hardware home button.
Doesn't this mean your main activity is destroyed by the back button and new one is initialized?
componentWillUnmount should execute when exiting the app.
If you do not want to destroy MainActivity put the implementation below for backpress.
@Override
public void invokeDefaultOnBackPressed() {
moveTaskToBack(true);
}
@vjeranc That does make the back button work as I expect, which is great!
However, it does surprise me that this is not the default behaviour. As the app appears to go into the background in the exact same way as pressing the home button (much like iOS).
Perhaps it's just my misunderstanding of how it works on Android, but I would expect the root component to be instantiated only once unless the app is actually properly closed (swiped from the list of active apps).
If this is the case, it may well be worth documenting it somewhere if it isn't already because this could lead to some pretty horrible things happening.
@nouveller it is also a common pattern to put the removal of listeners in componentWillUnmount. Given that new 16.x react can call componentWillMount several times before rendering you should put the creation of listeners in the constructor (there are cases where putting the creation in componentDidMount will work better).
Yeah, that's a good point too. Luckily I'm already following that pattern but thanks for mentioning it.
To add to my original problem, I'm using redux-saga to handle HTTP requests amongst other tasks and the sagas are kicked off from the root componentDidMount, which resulted in Android running multiple instances of the sagas after using the back button, thus resulting in duplicate HTTP requests.
@nouveller Then you will still have the issue. On Android, user can manually kill the activity. Long pressing the Home button or by pressing the hardware square button results in a list of all background apps. If your app window is removed it will kill the activity (but not the app, at least not immediately).
What about putting this in androidManifest? Is this related / help? android:launchMode="singleTop" to the activity
@ms88privat I think this is the only sane option, because doing otherwise would enable accidental launch of another activity that would result in two activities (new one can be launched by some Intent). Although, user can still kill MainActivity (without killing your app), there's no way around it.
@vjeranc
16.x react can call componentWillMount several times
can you point to the source of this?
This causes things like event listeners to stack up.
@vjeranc but this shouldn't happen if the MainActivity got killed, right? If I understand @nouveller problem correctly, this is the main problem and the best case would be, to not even destroy it in the first place?
I noticed very similar behavior to this when using the home button. Oddly enough it was only on the initial launch of the app.
react-native init Testing
cd Testing
react-native run-android
Once the app launches minimize with the home button and launch it again. You can now hit the back button to close the second instance that was created and return to the first.
If you hit the back button enough to force the app the close this issue doesn't reoccur on additional launches.
android:launchMode="singleTop" seems to clear it up.
I am facing similar issue but with iOS. When the app goes to background by clicking the home button, now if we open app using the icon or from the background, then it gets rendered 2 times and then 3,4,5.... times.
I needed to combine the answers of @vjeranc and @ms88privat in order to get it working.
// MainActivity.java
@Override
public void invokeDefaultOnBackPressed() {
moveTaskToBack(true);
}
<!-- AndroidManifest.xml -->
<activity
...
android:launchMode="singleTop">
Works perfectly! thanks @birgernass !!
We encountered this issue on android on a project at work. The first time you installed the app, if you pressed home and then re-opened the app then the root component would re-initialize. The issue manifested with a video player, where you would hear two videos playing over the top of each other. The problem would compound each time you pressed home and re-opened the app - it was as if react-native was re-instantiating multiple copies of the app on top of each other! If you killed the app completely, then the problem would not re-manifest. It only happened the first time you installed, and if you had not yet closed the app. @birgernass that fix seems to resolve the issue though!
Have the same issue (for me react-redux connect will be stacking up and firing multiple times , for each pressing home button and relaunching app ) , also it looks like each time when app goes from background to active state componentDidMount is called, how ever when we press home button componentWillUnmount is not called.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. If you think this issue should definitely remain open, please let us know why. Thank you for your contributions.
Why closing the issue? I am having exactly the same issue here with RN 0.51.0. It happens when I click the home button, background the app and then foreground it again. It will create many many instances of the root component and will eventually cause the app to be very slow. Strangely enough, this only happens in debug mode for me. Have you guys observed the same behavior? @kozillla @birgernass
@tianxind , yup same here.
As a work around I set
android:launchMode="singleTop">
That seems to help with this issue.
Most helpful comment
I needed to combine the answers of @vjeranc and @ms88privat in order to get it working.