React-Native NetInfo dosen't work after once did work? - react-native

I'm new at react-native I have a problem about internet connection check. When I open my app and disconnect for internet(wifi or cellular both) I got a alert message that's what I want. But when I connect again to internet and try again I see in my console state is again false. NO way to see true. Where did I do a fault?
` const [isInternetReachable, setInternetReachable] = useState(true)
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
if (!(state.isInternetReachable === null)) {
setInternetReachable(state.isInternetReachable);
}
});
},[])
useEffect(() => {
chechConnection();
}, [isInternetReachable]);
const chechConnection = () => {
if(isInternetReachable === false) {
Alert.alert(
'Internet fail',
'Try again.',
[
{text: 'Try Again', onPress: chechConnection},
{text: 'Exit', onPress:() => RNExitApp.exitApp()},
],
{ cancelable: false }
)
}
};`

The useEffect which is responsible for setInternetReachable will only run once, no matter what.
Try like this:
const [connected, setConnected] = useState();
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
console.log("Connection type", state.type);
console.log("Is Internet Reachable?", state.isInternetReachable);
if (connected !== state.isInternetReachable)
setConnected(state.isInternetReachable);
});
return () => unsubscribe();
}, [connected]);

I found a problem its when I use İf condition or switch my connection state doesn't change. When I don't use switch or if connection state works well.

Related

How should I disconnect from signalR when leaving a screen in react-native

In my app I have a few screens that use signalR like that. That function is called useEffect function and it works:
const setupSignalR = () =>
{
SecureStore.getItemAsync("token").then(tk => {
let connection = new HubConnectionBuilder()
.withUrl("URL", {
accessTokenFactory: () => tk
})
.build();
connection.on("Update", function (message) {
//DOSTUFF
});
connection.start()
.then(() => console.log("connection started"))
.catch(err => console.log("connecting hub failed err is : ", err));
});
}
The problem is that if I leave the screen the connection stays open, and when I return to the screen I open another connection which means I now have 2 connections open at the same time.
I know that signalR has a stop function that I can call, so I tried to use the navigation listeners like that, but they aren't called:
useEffect(() =>
{
Load();
setupSignalR();
const unsubscribe = navigation.addListener('focus', () => {
});
const sub = navigation.addListener('blur', () => {
console.log("============");
});
}, [navigation]);
I generally leave a screen by pressing the back-button or by using navigation.navigate();
return () => {
connection.stop();
}
Works.
I think you need connection.
const setupSignalR = () => {
SecureStore.getItemAsync("token").then(tk => {
let connection = new HubConnectionBuilder()
.withUrl("URL", {
accessTokenFactory: () => tk
})
.build();
connection.on("Update", function (message) {
//DOSTUFF
});
connection.start()
.then(() => console.log("connection started"))
.catch(err => console.log("connecting hub failed err is : ", err));
return connection;
});
}
useEffect(() => {
Load();
let connection = setupSignalR();
const unsubscribe = navigation.addListener('focus', () => {
});
const sub = navigation.addListener('blur', () => {
console.log("============");
});
return () => {
connection.stop();
}
}, [navigation]);

Expo Push Notification Foreground not working

Background Notifications: Working
Killed notificiations: Working
Token generation: Working
Permissions: Verified and working
What should I do to troubleshoot this properly? I have tried other methods of handling, and I believe I tried adding a notification property to app.json but nothing worked to my knowledge.
Thanks for your time!
// imports redacted, but contain expo notification, device etc
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export default function App() {
const [expoPushToken, setExpoPushToken] = useState<string|undefined>('');
const [notification, setNotification] = useState<any>(false);
const notificationListener = useRef<any>();
const responseListener = useRef<any>();
useEffect(() => {
if(Device.isDevice){
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
// 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 => {
console.log(response);
});
return () => {
Notifications.removeNotificationSubscription(notificationListener.current);
Notifications.removeNotificationSubscription(responseListener.current);
};
} else {
//
}
}, []);
return(view stuff)
}
// outside of functional component
async function registerForPushNotificationsAsync() {
let token;
if (Constants.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getExpoPushTokenAsync({ experienceId: '#Expo-project-name' })).data; // commented project name for security
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
return token;
}
the fix to this solution is in the experienceId
Make sure your experienceID matches EXACTLY what your expo project name is.
I had mine where the #username/project-name 'project-name' portion was lowercase, but my project was actually named in CAPITAL letters, so #username/PROJECT-NAME
That's the fix!

How to write a JEST test for a useEffect with timers?

I am using #react-native-community/netinfo to detect the app connectivity state and showing a message when connection is lost. How do I write a test for the following code that's in a useEffect to make sure that the message is showing/hiding and the cleanup works?
const { isConnected } = useContext(ConnectionContext);
...
useEffect(() => {
const snack = setTimeout(() => {
if (!isConnected) {
showMessage({
autoHide: false,
message: 'Please try again later',
});
}
}, 10000);
const hideSnack = setTimeout(() => {
if (isConnected) hideMessage();
}, 5000);
return () => {
clearTimeout(snack);
clearTimeout(hideSnack);
};
}, [isConnected]);
I have tried something like this to check if the app is connected
jest.mock('#react-native-community/netinfo', () => ({
...jest.requireActual('#react-native-community/netinfo'),
useNetInfo: () => ({
isConnected: true,
})
}));
You can use jest fake timers to control the time:
at the top use
jest.useFakeTimers();
then when you need to advance time by certain amount use :
jest.advanceTimersByTime(100);

AppState removeEventListener not removing listener

Simple code:
useEffect(() => {
AppState.addEventListener("change", handleChangeEvent);
return (() => {
console.log("REMOVING EVENT LISTENER");
AppState.removeEventListener("change", handleChangeEvent);
});
}, []);
const handleChangeEvent = () => {
console.log("EVENT LISTENER FIRING");
};
Navigate to new screen:
const changeScreen = () => {
return props.navigation.navigate("MainView", {})
}
When arriving at new screen: The REMOVING EVENT LISTENER fires. But in this new screen, the event listener is still listening.
Why is it not being removed?
try using useFocussedEffect instead,
useFocusEffect(
React.useCallback(() => {
AppState.addEventListener("change", handleChangeEvent);
return () => {
AppState.removeEventListener("change", handleChangeEvent);
}
}, [])
);

Using OneSignal.registerForPushNotifications, then approve not registering

I am user OneSignal in combination with React-Native to setup the push notifications. In my app, users need to be logged in, in order receive push notifications (or to give permission).
The issue that I am having, is that when the user logs in, they get my custom popup, asking if they would like to receive messages. If they click yes, they iOS popup shows, asking for permission.
When I click yes again, and check the OneSignal users dashboard, to see which users have registered for the push notifications. I see mine, but as opted out, which is not correct.
So I am wondering if there is a bug in the code of OneSignal (already in contact with them, but is going slow) or my code is wrong.
I am using;
React-native 0.56
react-native-onesignal: 3.2.7
The following code is what I currently have:
componentWillMount() {
OneSignal.init("{key}", {kOSSettingsKeyAutoPrompt : false});
AsyncStorage.multiGet(['firstTime', 'subscribed']).then((response) => {
this.setState({
firstTime: JSON.parse(response[0][1]) != null ? JSON.parse(response[0][1]) : true,
subscribed: JSON.parse(response[1][1]) != null ? JSON.parse(response[1][1]) : false
});
setTimeout(() => {
if (Platform.OS === 'android' && !this.state.subscribed && this.state.firstTime) {
OneSignal.setSubscription(false);
}
}, 200);
});
OneSignal.addEventListener('received', this.onReceived);
OneSignal.addEventListener('opened', this.onOpened);
OneSignal.addEventListener('ids', this.onIds);
}
componentDidMount () {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange);
AsyncStorage.getItem('firstTime').then((result) => {
!this.isCancelled && this.setState({
firstTime: JSON.parse(result) != null ? JSON.parse(result) : true
})
});
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setTimeout(() => {
if (!this.state.subscribed && this.state.firstTime) {
Alert.alert(
I18n.t('notifications title'),
I18n.t('notifications subtitle'),
[
{text: I18n.t('notifications no'), onPress: () => {
setTimeout(() => {
AsyncStorage.multiSet([['firstTime', JSON.stringify(false)], ['subscribed', JSON.stringify(false)]]);
}, 100)
}, style: 'cancel'},
{text: I18n.t('notifications ok'), onPress: () => {
if (Platform.OS === 'android') {
OneSignal.setSubscription(true);
}else if (Platform.OS === 'ios') {
// OneSignal.setLogLevel(6, 0);
OneSignal.registerForPushNotifications();
}
setTimeout(() => {
AsyncStorage.multiSet([['firstTime', JSON.stringify(false)], ['subscribed', JSON.stringify(true)]]);
}, 100)
}}
],
{ cancelable: false }
)
}
}, 500)
}else{
OneSignal.setSubscription(false);
}
SplashScreen.hide();
});
};
Hope you guys can help out.
Cheers,
Ah, I found the cause of the issue. The second setSubscription(false) in the else statement was unsubscribing even-though I didn't.
So removing the else is fixing it for me.