I have added the listener in the following way(tried putting in both constructor and componentDidMount): AppState.addEventListener('change', this._handleAppStateChange);
And the removed the listener the following way in componentWillUnmount method:
AppState.removeEventListener('change', this._handleAppStateChange);
And in the callback function:
_handleAppStateChange = (nextAppState) => {
setTimeout(() => {
alert('App state: ' + this.state.appState);
alert('Next App state: ' + nextAppState);
}, 0);
}
It alerts several times. Its not removing the listener configured once. Please let me know if someone is aware of it ?
According to the latest docs (September 2021 v0.65+) removeEventListener
is deprecated
The docs now recommend using the remove
function on the subscription object (EmitterSubscription
) which is returned from AppState.addEventListener
.
Example usage:
const subscription = AppState.addEventListener('change', (appState) => {
if (appState !== 'active') {
return;
}
// Run custom logic
subscription.remove();
});
You should use API like now on
useEffect(() => {
const myListener = AppState.addEvenListener('change', this.someHandler)
return () => {
myListener.remove()
}
}, [])
Faced the same issue these last days. I've finally managed it by deporting my app state management to my App.js component and created a service manager.
Here quickly how my App.js
look like:
import {AppState } from "react-native";
import {AppStateService} from "YOUR_PATH_TO_THE_NEXT_FILE";
export default function App() {
// Listen to app state
AppStateService.init();
useEffect(() => {
AppState.addEventListener('change', AppStateService.getInstance().handleAppStateChange);
return (() => {
AppState.removeEventListener('change', AppStateService.getInstance().handleAppStateChange);
})
}, []);
return (/*Rendering stuff (navigation, error boundary, ...*/);
}
AppStateService.js
:
/**
* Class to allow us to refer to the app state service
*/
export class AppStateService {
static instance;
static STATE_ACTIVE = 'active';
static STATE_INACTIVE = 'inactive';
static STATE_BACKGROUND = 'background';
static STATE_NOT_LAUNCHED = 'not_launched';
previousState = AppStateService.STATE_NOT_LAUNCHED;
currentState = AppStateService.STATE_ACTIVE;
handlers = {};
appLaunchId = 0;
/**
* @returns {AppStateService}
*/
static getInstance() {
if(!this.instance){
this.instance = new AppStateService();
}
return this.instance;
}
static init = () => {
// This func need to be call in the App.js, it's just here to create the instance
const instance = AppStateService.getInstance();
instance.appLaunchId = new Date().getTime() / 1000;
}
handleAppStateChange = (nextState) => {
if(nextState !== this.currentState) {
this.previousState = this.currentState;
this.currentState = nextState;
for (const [key, handler] of Object.entries(this.handlers)) {
handler(nextState);
}
}
}
getCurrentState = () => {
return this.currentState;
}
getPreviousState = () => {
return this.previousState;
}
addStateHandler = (key, handler) => {
this.handlers[key] = handler;
}
hasStateHandler = (key) => {
if( this.handlers[key] ){
return true;
}
return false;
}
removeStateHandler = (key) => {
delete this.handlers[key];
}
}
and now here is how to call it from anywhere you want in your app components:
export default class RandomComponent extends React.Component {
componentDidMount() {
// Check app going background or not
this.handleAppStateChange = this.handleAppStateChange.bind(this);
AppStateService.getInstance().addStateHandler('myListenerCustomKey', this.handleAppStateChange);
}
componentWillUnmount() {
// Remove app state change listener
AppStateService.getInstance().removeStateHandler('myListenerCustomKey');
}
handleAppStateChange = (nextAppState) => {
console.log("I'm going to be -" + nextAppState + "- while I was -" + AppStateService.getInstance().getPreviousState() + "-");
}
}
This way you have the capability to listen everywhere in your app to the app foreground/inactive/background state and correctly subscribe/unsubscribe to those events.
I'm assuming you're using a class component by the looks of your code. The way I ended up solving it was by creating a pointer function that points to the actual function inside the scope of this
, without using .bind(this)
.
E.g.
// Actual Function
handleAppStateChange(state) {
// Work your magic!
}
// Pointer
handleAppStateChangeCall = (state) => this.handleAppStateChange(state);
// Setup listener event
setupAppStateListener() {
AppState.addEventListener("change", this.handleAppStateChangeCall);
}
// Clear listener event
clearAppStateListener() {
AppState.addEventListener("change", this.handleAppStateChangeCall);
}
// Mounted Hook
componentDidMount() {
setupAppStateListener();
}
// Unmount Hook
componentWillUnmount() {
clearAppStateListener()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With