current I'm using AppState to know when the app is coming back to the active state and inside there I'm making an API call with redux see cold below.
useEffect(() => {
const subscription = AppState.addEventListener("change", (nextAppState) => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
getTimes();
dispatch(getLocation());
dispatch(getCity());
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
});
return () => {
subscription.remove();
};
}, []);
It works fine on an iPhone but when I deploy it to an android the API calls keep getting called and never stop if I comment out the redux call and redeploy it to an android phone everything works fine and it doesn't keep refreshing.
Related
I'm trying to detect if user is connected to the internet. My problem occurs when user is connected to wifi but still have access to mobile internet, then when user turn off wifi (still connected to mobile internet) isInternetReachable state changes to false and then back to true. Anyone encountered something simiral and got some solution for this? I'm not testing on emulator btw.
export function useNetworkHook() {
const [network, setNetwork] = useState(true);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(data =>
handleConnectivityChange(data.isConnected),
);
return () => {
unsubscribe;
};
}, []);
function handleConnectivityChange(isConnected: boolean | null) {
if (isConnected === null || isConnected === true) {
setNetwork(true);
} else {
setNetwork(isConnected);
}
}
return {network};
}
Is there a way to monitor when a phone has their location settings (not permissions) turned on or off?
I know there's a way to check with react-native-device-info with the code below, but is there a way to add a listener to constantly check?
DeviceInfo.isLocationEnabled().then((enabled) => {
// true or false
});
When users change location setting, Users open setting page. It means you should know if app is foreground or background.
In that case, you can use AppState (https://reactnative.dev/docs/appstate).
To monitor when a phone has their location settings (not permissions) turned on or off, You can implement like this.
import {AppState} from 'react-native';
useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState) => {
// if app is foreground.
if (nextAppState === 'active') {
DeviceInfo.isLocationEnabled().then((enabled: boolean) => {
// true or false
});
}
});
return () => {
subscription.remove();
};
}, []);
Description:
When the app is running in a background state or Locked, When the user unlock or foreground the app, useNetInfo(); hook return as isConnected as false. Even I tried to re-fetch the state still using NetInfo.fetch() return the same state.
It's happening in Android real device connected to Wifi
Package Name:
"#react-native-community/netinfo": "^7.1.2",
Code:
const netInfo = useNetInfo();
const [show, setShow] = useState(false);
useEffect(() => {
setShow(!(netInfo.isConnected && netInfo.isInternetReachable));
}, [netInfo]);
useEffect(() => {
fetchConnection();
}, []);
const fetchConnection = () => {
NetInfo.fetch().then((state: any) => {
setShow(!(state.isConnected && state.isInternetReachable));
});
};
I fixed this issue by reverting the package version into "#react-native-community/netinfo": "5.9.7",
Also, change the androidXCore version into 1.6.0. Now it's working as expected.
Reason:
Due to the hibernation features changes in androidXCore version 1.7.0. Netinfo does not return the state properly if the app is in a hibernation state. Please fix this issue in the upcoming release. Thanks.
add navigation focus event listener and check for network update whenever the screen is focused.
Please try this, it's from the package npm site
useEffect(() => {
const subAppState = AppState.addEventListener("change", async (nextAppState) => {
if (IS_IOS_DEVICE && nextAppState=='active') {
let newNetInfo = await NativeModules.RNCNetInfo.getCurrentState('wifi');
//your code here
}
});
const unsubNetState = NetInfo.addEventListener(state => {
//your code here
});
return () => {
if (subAppState) {
subAppState.remove();
}
unsubNetState();
};
},[]);
My app asks permission to use location services.
If a user denies permission, they can click a button to go to the settings page and grant permissions.
On ios, they are given the option to return directly to the app. on Android, I think they can do something similar.
Is there a way to detect arriving back at the app so I can check their permissions again?
I've tried with React Navigation's useFocusEffect hook:
useFocusEffect(
React.useCallback(() => {
console.log("navigated")
return () => getPosition()
}, [])
)
But unfortunately, that only works when navigating between screens/routes in the app.
Is there a way to detect the app transitioning from background to foreground?
You can track app state with https://reactnative.dev/docs/appstate
in pseudo-code: if previously 'background' and now 'active' then run your effects
Ciao, when I want to manage permission, I use react-native-permissions. In this way you can manage all permissions directly in your app without exit from it.
Works well for iOs and Android. In your case, you could something like:
import {check, PERMISSIONS, RESULTS} from 'react-native-permissions';
check(<permission you need>)
.then((result) => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
'This feature is not available (on this device / in this context)',
);
break;
case RESULTS.DENIED:
console.log(
'The permission has not been requested / is denied but requestable',
);
break;
case RESULTS.GRANTED:
console.log('The permission is granted');
break;
case RESULTS.BLOCKED:
console.log('The permission is denied and not requestable anymore');
break;
}
})
.catch((error) => {
// …
});
Use this hook:
// hooks/useAppIsActive.ts
import { useCallback, useEffect, useRef } from "react";
import { AppState } from "react-native";
export default (callback: Function) => {
const appStateRef = useRef(AppState.currentState);
const handleAppStateChange = useCallback((nextAppState) => {
if (
appStateRef.current.match(/inactive|background/) &&
nextAppState === "active"
) {
callback();
}
appStateRef.current = nextAppState;
}, []);
useEffect(() => {
AppState.addEventListener("change", handleAppStateChange);
return () => {
AppState.removeEventListener("change", handleAppStateChange);
};
}, []);
};
And from the component you want to detect "the come back", pass by parameter the callback you want to run:
const MyView = () => {
const requestLocationAccess = useCallback(() => {
// request permissions...
}, [])
useAppIsActive(() => requestLocationAccess());
}
Is this at all possible? I'm currently using react-native-track-player to stream audio files and I would love to be able to store the last position when my users exit the app and resume when they re-open (e.g. similar to how Spotify works)
Right now I'm tracking this info via a simple interval:
this.keepTime = setInterval(async () => {
const state = await TrackPlayer.getState()
if (state == TrackPlayer.STATE_PLAYING) {
const ID = await TrackPlayer.getCurrentTrack()
const position = await TrackPlayer.getPosition()
await AsyncStorage.setItem(ID, String(position))
}
}, 10000)
Problem is, I need to clear the interval when my app moves to the background or else it will crash. I would also much rather only need to call this code once as opposed to periodically if that is possible.
I know I could use headless JS on android but the app is cross platform, so my iOS user experience would be lesser.
Any suggestions?
I think you can use componentWillUnmount() function for this.
You could add a listener to get the App State and then log the position when it goes to background.
class App extends React.Component {
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this.handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
this.saveTrackPosition();
}
handleAppStateChange = (nextAppState) => {
if (nextAppState.match(/inactive|background/) && this.state.appState === 'active') {
this.saveTrackPosition();
}
this.setState({appState: nextAppState});
}
saveTrackPosition = () => {
if (state == TrackPlayer.STATE_PLAYING) {
const ID = await TrackPlayer.getCurrentTrack()
const position = await TrackPlayer.getPosition()
await AsyncStorage.setItem(ID, String(position))
}
}
}