Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appstate keep on getting change in React native in Android

I am working on React native project and there I am taking location permissions. Also I have to track location permissions always like if user has given permission access after install the application and then after sometime user goes to the app settings in device settings and disable/revoked the permissions. Again once app comes from background to foreground, I have to check permission based on that, Needs to show the messages.

So that, I am using Appstate. But, In Android strangely, After installed the application, If user denied the permission with "Dont show again" checkbox, Then Appstate getting keep on changing with background and active always. It is keep on loop.

componentDidMount = async () => {
    AppState.addEventListener('change', this.handleAppStateChange);
  };

  componentWillUnmount() {
    AppState.removeEventListener('change', this.handleAppStateChange);
    Geolocation.clearWatch(this.watchID);
  }

  handleAppStateChange = async nextAppState => {
    const {appState} = this.state;
    console.log('nextAppState -->', nextAppState);
    console.log('appState -->', appState);
    if (appState === 'active') {
      // do this
      this.showLoader();
      await this.requestAndroidLocationPermission();
    } else if (appState === 'background') {
      // do that
    } else if (appState === 'inactive') {
      // do that other thing
    }

    this.setState({appState: nextAppState});
  };

requestAndroidLocationPermission = async () => {
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        {},
      );
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        this.getLatitudeLongitude();
      } else if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
        this.hideLoader();
        this.setState({
          errorMessage: 'Location permission is denied',
          isLoading: false,
        });
      } else {
        this.hideLoader();
        this.requestAndroidLocationPermission();
      }
    } catch (err) {
      console.warn(err);
    }
  };

It is keep on printing (loop) after denied permission with Don't show again

appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active

It goes on and never stop.

How to handle this? Any suggestions?

like image 993
Anilkumar iOS - ReactNative Avatar asked Nov 13 '19 07:11

Anilkumar iOS - ReactNative


People also ask

Why is React Native slow on Android?

Slow app launch is another issue of React Native apps. If your app opens too slowly, you probably have too many dependencies in your app and you're using slow components. Try to use fast, high-performance components and decrease the number of dependencies in your app.

How do I get rid of addEventListener AppState?

Use the remove() method on the EventSubscription returned by addEventListener() .

What is the use of React Native appstate?

AppState is frequently used to determine the intent and proper behavior when handling push notifications. React Native AppState will provide you the following different App states: background – The app is running in the background. The user is either: [Android] on another Activity (even if it was launched by your app)

Why does the App State say current state is active?

This example will only ever appear to say "Current state is: active" because the app is only visible to the user when in the active state, and the null state will happen only momentarily. If you want to experiment with the code we recommend to use your own device instead of embedded preview. This event is received when the app state has changed.

What is inactive state in Android App State?

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 AppState.addEventListener ( 'change', this ._handleAppStateChange); In this Example, we will see how to know the application status using the AppState.

How do I update the user status in the appstate?

You can easily update the user status according to the change in the AppState — e.g., online when the user is interacting with the app, when the app is currently active, or when the app is in the foreground, and offline when the user puts the app in the background or closes it.


3 Answers

I had the same problem. Do not use AppState. Is faulty.

the problem lies within RN's definition of "background". react-native uses android's activity (the holder of the UI thread and where your UI lives) onPause callback as the trigger for sending the "background" signal. But, onPause is called everytime SOMETHING comes in front of your activity's view hierachy, like Dialogs (like the permission box), other activities (like a file picker), etc; for android react-native, "background" means "shadowed by a foreign UI element/android task" rather than "paused and sent to background to do something else", thus causing the loops you see. The shortest solution is to override onPause in your ReactActivity, and add control conditions to make sure super.onPause is only called when you are actually going to background, like checking your task stack, or if the permission dialog is being called, so you avoid this kind of loop/faulty call. A second option would be to provide your own app lifecycle event instead, with clear triggering conditions.

like image 177
Fco P. Avatar answered Oct 04 '22 02:10

Fco P.


today I had a similar problem.

I could solve it using "focus" in android and "change" in ios.

I have a custom hook like this:

import { useEffect } from 'react';
import { AppState, Platform } from 'react-native';


const focusEvent = Platform.OS === 'ios' ? 'focus' : 'change';

const useLocationListener = () => {


  useEffect(() => {
    AppState.addEventListener(focusEvent, handleAppStateChange);

    getLocationAsync();
    return () => {
      AppState.removeEventListener(focusEvent, handleAppStateChange);
    };
  }, []);

  const handleAppStateChange = (nextAppState: string) => {
    if (nextAppState === 'active') {
      getLocationAsync();
    }
  };

  const getLocationAsync = async () => {
    const { canAskAgain, status } = await Permissions.getAsync(
      Permissions.LOCATION
    );

    if (canAskAgain) {
      const response = await Permissions.askAsync(Permissions.LOCATION);
       // handle location 
    } 
       // handle location with "status"
    
  };
};

export default useLocationListener;
like image 45
devrchancay Avatar answered Oct 04 '22 03:10

devrchancay


You can use a flag that check whether app should handle background or it's just a permission call.

const shouldHandleBackground = useRef(true)

const handler = (state) => { 
    if (state === 'active' && shouldHandleBackground.current) { 
        doStuff() 
    }
}

// when calling for permisson make the flag false

shouldHandleBackground.current = false
await Android.permission.LocationPermission()
shouldHandleBackground.current = true

and after permission request you can make flag true

like image 25
TheEhsanSarshar Avatar answered Oct 04 '22 03:10

TheEhsanSarshar