How to detect when a React Native app is opened? - react-native

My React Native app wants to synchronize its local data with an API when the user opens the app. This should happen whenever the user returns to the app, not just when it first starts. Essentially, what I would like is the equivalent of AppDelegate's applicationDidBecomeActive callback, so that I can run synchronization code there. Obviously, I would like to do this in React Native instead.
As far as I can tell, the componentWillMount / componentDidMount callbacks on the root component only run when the app is initially loaded, not after the user leaves the app and comes back later (without explicitly quitting the app).
I thought that the AppState API would provide this functionality, but its change listeners don't fire in this case, either.
This seems like obvious functionality to have, so I must be missing something glaringly obvious. Help!

I fixed this by using the AppStateIOS API instead of AppState. The later works as expected on iOS devices: when the app goes to the background the "change" event fires, and again when it comes back to the foreground. The AppState API doesn't seem to fire the "change" event at all on an iOS device, as of React Native v0.18, as far as I can tell. The project's conventions suggest that AppState should be a cross-platform wrapper around AppStateIOS, but that doesn't seem to be the case here.
The following example should demonstrate the issue:
React.createClass({
componentDidMount() {
AppStateIOS.addEventListener('change', state =>
console.log('AppStateIOS changed to', state)
)
AppState.addEventListener('change', state =>
console.log('AppState changed to', state)
)
},
render() {
return <View/>
}
})
When this component is mounted into a React Native app, you will see "AppStateIOS changed to ..." when closing and opening the app. You will never see "AppState changed to ..." as far as I'm aware.
Update
It appears that this was fixed in React Native recently (as of v26). You can now use AppState as advertised on iOS and Android.

I don't think that you're missing something. This functionality is just not provided by react-native out of the box. Maybe the idea was to simplify, since for most apps it's enough to perform data sync when the app returns to foreground.
You can either create your own native module which provides this functionality or you can go with a simple (sort of hacky?) solution:
In your AppDelegate save a reference to the react root view:
#interface AppDelegate()
#property (nonatomic, strong) RCTRootView *rootView;
#end
When initializing the view, set your property and use it:
self.rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:#"MyApp"
initialProperties:nil
launchOptions:launchOptions];
Now implement the AppDelegate methods for handling the active state like you would do in your iOS apps and pass the information as props:
-(void)onAppActiveStateChanged:(BOOL)appBecameActive
{
NSMutableDictionary *newProps = [NSMutableDictionary dictionary];
if (self.rootView.appProperties != nil) {
[newProps addEntriesFromDictionary:self.rootView.appProperties];
}
newProps[#"appDidBecomeActive"] = #(appBecameActive);
self.rootView.appProperties = newProps;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[self onAppActiveStateChanged:NO];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self onAppActiveStateChanged:YES];
}
On the JavaScript side, do whatever you need when the props change:
componentWillReceiveProps(nextProps) {
if (nextProps.appDidBecomeActive) {
alert('app did become active');
}
}
I'm not sure if this is the best approach, but it's supposed to work...

I think you answered the question your self already. Why don't you use a combination of componentWillMount and AppState -> 'change'? That should cover all cases. I use this for syncing my app via CodePush.

Related

Expo react native can I just use navigation instead of deep linking to go to another page once notification tapped?

According to expos documentation, if I want user to navigate to another screen after user taps the notification received I should use deep linkedin. thats also the suggested method in all similar posts Ive seen. In my case I just do the following:
useEffect(()=> {
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
if (response) {
navigation.navigate("Page 1")
}
});
}, [])
It works fine, when I have app open or closed. I wonder if I just dont understand something and there will be issues as everywhere I read deep linking. Thanks!

React Native Linking eventListener crashes with deep linking

I am trying to implement deep linking/universal linking with my React Native app and it works good so far, but one thing. I have an eventListener and getInitialUrl in my app.js like so:
useEffect(() => {
Linking.addEventListener("url", (url) => handleInvite(url));
Linking.getInitialURL().then((url) => handleInvite(url));
}, []);
getInitialUrl works fine and the app opens and gets the url. This function is when the app is not active in the background. However, when the app is in the background the eventListener gets fired and the app crashes immediately. I tested it with and without and the problem is the eventListener, but I don't know why.
The app crashes immediately and I can't find any info on this problem. So any help would be much appreciated.
This is tested on iOS.
So I found the problem. Basically the addEventListener returns an array and not a string. So when using that in the function it caused the app to crash.
The right way to add the eventListener is like this:
Linking.addEventListener("url", event => handleInvite(event.url));
It is weird that no where in the docs this example is given. But it works now atleast!

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..;
}
}
}

React Native Deep Link Event Listener Not firing

componentDidMount() {
Linking.addEventListener('url', event =>
this.handleOpenURL(event.url));
Linking.getInitialURL().then(url => {
console.log('url===',url);
if(url)
this.handleOpenURL(url);
});
}
handleOpenUrl is never called if the app is already open and I try to change the deep link Url. If the app is already not open then it opens the app and gets the URL.
Actually it was the issue with FBSDK overriding the method.
This solution worked for me.
https://github.com/react-navigation/react-navigation/issues/798#issuecomment-290363058
I was having this issue working on a detached Expo project because I had added the suggested code from https://facebook.github.io/react-native/docs/linking.html to the *AppDelegate.m but Expo already provides slightly different functions for handling URL events. Removing the code from the React Native docs made it work for me.

React Native Lottie: Cannot read property 'play' of undefined

I'm using the Lottie imperative API to display a looping animation.
The imperative API works fine on all of my components except one that uses React Native Sound. I assume the problem is that both libraries are called with .play(). Is this possible?
Lottie: this.animation.play();
React Native Sound: this.sound.play()
After calling the Lottie method, I'm getting the Error Message:
Cannot read property 'play' of undefined
Any Ideas?
Thanks in advance.
I eventually figured it out. React Native Sound of course wasn't the problem - it might have just delayed the initialization of Lottie and thus animation was still undefined when I called it.
The solution is to build in a timer and was already suggested in this thread: https://github.com/airbnb/lottie-react-native/issues/21
componentDidMount() {
this.initAnimation();
}
initAnimation(){
if (!this.animation){
setTimeout(() => {
this.initAnimation();
}, 100);
} else {
this.animation.play();
}
}