Expo.js: Capture event when my app goes into the background? - react-native

If I'm using my app and then switch out of it using the home button, app switcher, etc, Is there a way to reliably run some code when this event is detected? I want to do some tasks such as cancelling timers, scheduling notifications, and so on.

You can track app's state using AppState provided by 'react-native'
By using AppState.currentState you will know whether the app is in foreground, background, active or inactive. If you want to perform some tasks whenever the state changes you can use event listeners provided by AppState.
You can add the following logic in your Component to get it done -
componentDidMount() {
AppState.addEventListener('change', this.handleStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.handleStateChange);
}
handleStateChange will be called every time the app moves from foreground to background or vice-versa with the app's current state passed as an argument.
For further reference see the docs
Hope it helped.

Related

Even listener stops working when app is running in the background

I am making a React Native app which talks to Spotify via this module: https://github.com/cjam/react-native-spotify-remote
The module has an event listener which allows us to listen to Spotify player state changes, for example:
import { remote as SpotifyRemote } from "react-native-spotify-remote";
// and in a React component:
SpotifyRemote.addListener("playerStateChanged", (state) => {
console.log(state);
});
The above works as long as our RN app is in focus. However, when the app is in the background (i.e. we are on a different app or the phone is locked), the event listner no longer gets triggered.
I am expecting the event listener to get triggered even when the app is running in the background but it isn't doing that.
Is there anyway to make this work even the app is not in focus? Thank you!

React Native doesn't re-render when re-opening app

I have a functional component in my react native app that has an event listener watching the app state in order to clear out a cache when the app is closed. This works as expected, and I have logic written in a useEffect block to add/remove this event listener when the app is closed.
The functionality works perfectly when the user either navigates elsewhere in the app or closes the app, but when the app is re-opened after closing (not fully shutting down the app, just moving to a different app) the page is not re-rendered and so the event listener is not re-added.
I've attached the code in my useEffect below.
useEffect(() => {
if (!listenerRef.current) {
toggleEventListener(true);
}
return () => {
toggleEventListener(false);
};
}, []);
You can use appState from react-native to see foreground and background events
https://reactnative.dev/docs/appstate

How to use Blur event for AppState in react native

Please I need an example on how to use the 'blur' event for react native AppState. I am trying to respond to when the app is not in focus e.g. when the user pulls the notification drawer but I keep getting the error message Invariant Violation: Trying to subscribe to unknown event: "blur".
Based on the tags associated with the commit that this feature landed in (https://github.com/facebook/react-native/commit/d45818fe47c53a670db933cf805910e227aa79c9) it seems like that is only available starting in 0.61 and hasn't landed in a stable release yet. Make sure you're running 0.61.0-rc.0 or later.
According to documentation . Blur is [Android only]
"[Android only] Received when the user is not actively interacting with the app. Useful in situations when the user pulls down the notification drawer. AppState won't change but the blur event will get fired."
if you still want to use it for android you can use it with condition for android only
import { Platform } from "react-native";
........
componentDidMount() {
if (Platform.OS === "android") {
AppState.addEventListener("blur", this._handleAppStateBlur);
}
}
componentWillUnmount() {
if (Platform.OS === "android") {
AppState.removeEventListener("blur", this._handleAppStateBlur);
}
}
_handleAppStateBlur = () => {
console.log("blur");
};
According to the docs mentioned in the official react native documentation, there are three states supported by AppState:
active - The app is running in the foreground.
background - The app is running in the background. The user is either:
in another app
on the home screen
[Android] on another Activity (even if it was launched by your app)
[iOS] inactive - This is a state that occurs when transitioning between foreground & background, and during periods of inactivity such as entering the Multitasking view or in the event of an incoming call.
Since there is no such state as blur, therefore you are facing an error saying that it could not find such event.
Edit
You have to register blur as an event in your component lifecycle, but you have to be cautious here and have to determine the Platform before registering blur event as it is available in android only and not in ios.
To register an event you have to do this:
import React from 'react';
import {AppState} from 'react-native';
class HandlingEvents extends React.Pure.Component {
constructor(props) {
super(props)
// your state goes here...
}
componentDidMount() {
// your event will be registered here, when your component is mounted on // the screen.
// Be cautious here, make a platform check here so as to avoid discrepancies in ios devices
AppState.addEventListener('blur',this.handleBlurState)
}
componentWillUnMount() {
// your event will be removed here, when your component gets unmounted from the screen.
// Be cautious here, make a platform check here so as to avoid discrepancies in ios devices
AppState.removeEventListener('blur',this.handleBlurState)
}
handleBlurState = (nextAppState) => {
//this method will contain your entire logic, as to how you want to treat your component in this event.
// As per the docs, since the state of your app will not changed, therefore you can continue your logic here by checking if the state of your app is **change** or not..
if (AppState.currentState === "active" && nextAppState === "active") {
//whatever task you want to perform here..;
}
}
}

child_added on stackNavigator dont fire componentWillUnmount

StackNavigator does not trigger the componentWillUnmount event, so two listeners remain open.
Is there any way to capture the "OnTabChange" event to stop the listener?
If the user changes quickly from one tab to another, would this be the best option?
I have also thought about creating a listener in the App.js file so that it emits a message (with the snapshot) and capture this event by the screen that listener listens to.
I was think to made a listener in App.js like
firebase.authFirebaseListener = auth.onAuthStateChanged((user) => {
if (user) {
db.ref('users')
.child(auth.currentUser.uid)
.child('favorites')
.on('child_added', snapshotFavorites => {
## Send a signal with snapshotFavorites
## to refresh data if current screen need it.
})
....
}
}
componentWillUnmount() {
db.ref('users').child(auth.currentUser.uid).child('favorites').off()
db.ref('users').child(auth.currentUser.uid).off()
db.ref('users').off()
}
I'm not sure if it's the same thing, but I used react-navigation-is-focused-hoc and added a listener to the page I wanted to trigger ComponentWillMount and detect changes.
Give it a look, I used in the exact way the tutorial of it says in github.

Clear Previous Expo Push Notifications

I have my app in Expo pushing a notification when somebody sends a message, but if that person sends multiple messages a second notification is pushed.
Is there anything I can do to clear the previous notification, or simply update the notification instead of adding a second notification to the list?
Basically I need to force an override or dismiss previous notifications.
The approach I was hoping to use was to add a listener which cleared notifications before appending, but it seems that this only works when the app is in the foreground.
Is there a recommended approach to this currently?
You can clear any or all previous notifications in expo-notifications. Your question is not clear but I am guessing you want to clear all previous notification if new notification is received. You have to spot the best place when to clear notifications on your code. But here are some tips (use the following code in the useEffect hook) -
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
// DISMISS ALL NOTIFICATION AFTER INTERACTION
Notifications.dismissAllNotificationsAsync();
});
If you want to dismiss specific notification in that case, use dismissNotificationAsync(identifier: string): Promise method.
Just in case, if you want to dismiss all notifications when receiving a new one while the app is foregrounded (use the following code in the useEffect hook).
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
// setNotification(notification);
// DISMISS ALL NOTIFICATION UPON RECEIVING NOTIFICATION WHILE IN FOREGROUND
Notifications.dismissAllNotificationsAsync();
});
You can use Notifications.dismissAllNotificationsAsync() method or dismissNotificationAsync(identifier: string): Promise method anywhere you like but you need to spot where to best use them.
_handleNotification = (notification) => {
this.setState({notification: notification});
console.log('get notification', this.state.notification);
let localnotificationId = this.state.notification.notificationId;
setTimeout(function () {
Notifications.dismissNotificationAsync(localnotificationId);
}, 10000)
};
This is how I do in NodeJS