react-native
react-native copied to clipboard
AppState change listener called multiple times on Android
React Native version:
React Native Environment Info:
System:
OS: macOS 10.14.5
CPU: (8) x64 Intel(R) Core(TM) i7-3615QM CPU @ 2.30GHz
Memory: 197.04 MB / 16.00 GB
Shell: 5.3 - /bin/zsh
Binaries:
Node: 10.15.3 - /usr/local/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.9.0 - ~/.npm-global/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
Android SDK:
API Levels: 23, 25, 26, 27, 28
Build Tools: 26.0.2, 26.0.3, 27.0.0, 27.0.2, 27.0.3, 28.0.0, 28.0.0, 28.0.3
System Images: android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom
IDEs:
Android Studio: 3.3 AI-182.5107.16.33.5314842
Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
npmPackages:
react: 16.8.3 => 16.8.3
react-native: 0.59.8 => 0.59.8
npmGlobalPackages:
create-react-native-app: 1.0.0
react-native-cli: 2.0.1
react-native-git-upgrade: 0.2.7
Steps To Reproduce
- Open the app
- Press home button
- Open the app. You should see the alert once (this is fine)
- Press home button
- Open the app. The alert will be shown 2 times (this is not fine, it should be only once)
- Press home button
- Open the app. The alert will be shown 3 times (this is not fine, it should be only once) ...
Expected behaviour: The alert should be shown only once each time you open the app. This issue is happening only in Android. The alert is just an example, you can put anything inside and it will be called multiple times.
Simple code to reproduce:
import React, { Component } from "react";
import { AppState, Text } from "react-native";
export default class App extends Component {
state = {
appState: AppState.currentState
};
componentDidMount() {
AppState.addEventListener("change", this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener("change", this._handleAppStateChange);
}
_handleAppStateChange = nextAppState => {
if (
this.state.appState.match(/inactive|background/) &&
nextAppState === "active"
) {
alert("I should be shown only once");
}
this.setState({ appState: nextAppState });
};
render() {
return <Text>Current state is: {this.state.appState}</Text>;
}
}
@diazweb Hey,
I've tried to reproduce your issue with the given code. I get the proper one alert every time (tested on emu and real dev).
What device are you testing that on?
@Krizzu Hi,
I've tried the app in:
- Simulator Nexus 4 API 27 (8.1) with Google Apis x86
- Real device: BQ Aquaris E5 with Android 6.0.1
I just updated to React Native 0.59.9 and still the same issue, I'm going to update my node.js and JDK, update all the Android Studio packages and reinstall node_modules (just in case). EDIT: I've done this and still the same issue.
Thanks!
I have the same problem in Honor 9 STF-AL10
with Android 9.
but I observed AppState change listener called multiple times occur in exit App by double-click the Android Back button. AppState change listener only called one time if I exit App by click the Home button.
I met the similar issue when I was trying to receive share intent with my app.
New activity was created every time when the app was opened by intent and AppState listener was called the same number of times as the number of existing activities.
Changing android:launchMode
to "singleTask"
in AndroidManifest.xml fixed the issue for me.
But I don't know if your cases are the same as mine.
Also experiencing this behavior with React 0.59.
@diazweb have you tried 0.60 to see if it still repros this bug? Also, have you tried @kazyk 's solution of changing android:launchMode
?
Having same issues via emulator, API 29. Also happens on physical device, Note 10+ running Android 10, API 29.
changing android:launchMode
did not help.
only on Android not iOS
Pay attention to the native libs you're using and the methods you're calling. I fell on this issue while calling a native module method that was secretely spawning a new hidden activity. The problem right now is that I don't know how to distinguish between having a backgrounded app and having an activity spawned by my app, if I only could reach somehow the current activity name...
I'm sure many issues, like this #18575, are related
I had this issue and I think it was because the change listener was never being unregistered in componentWillUnmount because componentWillUnmount was never called. Fixed it with the solutoin here:
https://stackoverflow.com/questions/54482733/react-native-appstate-addeventlistener-registering-duplicate-events-on-resume-w/54484054#54484054
if (AppState._eventHandlers.change.size === 0) {
AppState.addEventListener('change', this.handleAppStateChange)
}
Now I have this typescript error:
src/screens/MapScreen/index.tsx:108:18 - error TS2339: Property '_eventHandlers' does not exist on type 'AppStateStatic'.
@diazweb Hey,
I've tried to reproduce your issue with the given code. I get the proper one alert every time (tested on emu and real dev).
What device are you testing that on?
I also get the alerts twice on my android devices.
Also experiencing this behavior with React 0.59.
@diazweb have you tried 0.60 to see if it still repros this bug? Also, have you tried @kazyk 's solution of changing
android:launchMode
?
I am using RN 0.60.4, and the bug still exists.
Do you all have the following code ever called during your testing?
componentWillUnmount() {
AppState.removeEventListener("change", this._handleAppStateChange);
}
Linking.addEventListener('url', this._handleOpenURL);
Also triggers multiple times.
I had this issue and I think it was because the change listener was never being unregistered in componentWillUnmount because componentWillUnmount was never called. Fixed it with the solutoin here:
https://stackoverflow.com/questions/54482733/react-native-appstate-addeventlistener-registering-duplicate-events-on-resume-w/54484054#54484054
if (AppState._eventHandlers.change.size === 0) { AppState.addEventListener('change', this.handleAppStateChange) }
Now I have this typescript error:
src/screens/MapScreen/index.tsx:108:18 - error TS2339: Property '_eventHandlers' does not exist on type 'AppStateStatic'.
YOU ARE A KING , SIR , THANK YOU, LIFE SAVING COMMENT
Just a warning, using AppState._eventHandlers.change.size
might cause crashes on iOS 13. Seems to be working fine on Android.
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
I resolved it by changing arrow function to function in this way:
AppState.addEventListener('change', myFunc)
function myFunc() { // some code}
I resolved it by changing arrow function to function in this way:
AppState.addEventListener('change', myFunc)
function myFunc() { // some code}
Thank you @MahmonirB , For anyone looking for answers, just check if you've used arrow function in the handler function. This is what was the source of my problem as well. No sooner did I change this to a normal function, a whole lot of my problems went away.
This is still happening, adding the ref check does fix this, but it does also complains about the missing type.
if (AppState._eventHandlers.change.size === 0) {
AppState.addEventListener('change', handleFocusChange)
}
Not solve issue, but less callbacks...
- Real device iPhone 13 - IOS 15.5
- Expo 43
- RN 0.64.3
const [appState, setAppState] = useState();
// APPSTATE HANDLER
const appHandler = (changeType) => {
if (changeType === "active" || changeType === "background") {
console.log("appHandler =>", changeType);
setAppState(() => changeType);
}
};
// APPSTATE LISTENER
useEffect(() => {
AppState.addEventListener("change", appHandler);
return () => AppState.removeEventListener("change", appHandler);
}, []);
// APPSTATE CALLBACKS
useEffect(() => {
const isForeground = appState === "active";
const isBackground = appState === "background";
// YOUR CODE HERE...
}, [appState]);
I had this issue and I think it was because the change listener was never being unregistered in componentWillUnmount because componentWillUnmount was never called. Fixed it with the solutoin here:
https://stackoverflow.com/questions/54482733/react-native-appstate-addeventlistener-registering-duplicate-events-on-resume-w/54484054#54484054
if (AppState._eventHandlers.change.size === 0) { AppState.addEventListener('change', this.handleAppStateChange) }
Now I have this typescript error:
src/screens/MapScreen/index.tsx:108:18 - error TS2339: Property '_eventHandlers' does not exist on type 'AppStateStatic'.
I had this issue and I think it was because the change listener was never being unregistered in componentWillUnmount because componentWillUnmount was never called. Fixed it with the solutoin here:
https://stackoverflow.com/questions/54482733/react-native-appstate-addeventlistener-registering-duplicate-events-on-resume-w/54484054#54484054
if (AppState._eventHandlers.change.size === 0) { AppState.addEventListener('change', this.handleAppStateChange) }
Now I have this typescript error:
src/screens/MapScreen/index.tsx:108:18 - error TS2339: Property '_eventHandlers' does not exist on type 'AppStateStatic'.
When there are two 'change' listeners registered back and forth with same condition to determine add it or not, e.g.
if (AppState._eventHandlers.change.size === 0)
{
AppState.addEventListener('change', handleListenerOne) ...
}
...
if (AppState._eventHandlers.change.size === 0)
{
AppState.addEventListener('change', handleListenerTwo) ...
}
the first listener make AppState._eventHandlers.change.size becomes 1 and the latter one will never be added.
A workaround could be by adding a boolean to AppState._eventHandlers.change instead, e.g.
if (!AppState._eventHandlers.change._isListenerOneSubscripted)
{
AppState.addEventListener('change', handleListenerOne) ...
AppState._eventHandlers.change._listenerOneSubscripted = true
}
...
if (!AppState._eventHandlers.change._isListenerTwoSubscripted)
{
AppState.addEventListener('change', handleListenerTwo) ...
AppState._eventHandlers.change._listenerTwoSubscripted = true
}
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.