React native navigation works on Expo Go simulator but not web - react-native

I'm using React Navigation for my RN app. Elsewhere in my app, I'm able to do navigation.navigate('LoggedOut') fine, for example, during my logging out flows to bring users back to my logged-out splash screen.
I just tried doing this in a new place, like so:
export function ChildComponent({navigation, ...}) {
const logoutHandler = async () => {
dispatch(setLoginToken(''))
await AsyncStorage.setItem('session_token', '')
navigation.navigate('LoggedOut') // <- Key broken line
}
const swipeAction = async () => {
...
if ([logic calculating that the user's session has expired]) {
setTimeout(async function() {await logoutHandler()}, 3000);
}
}
return (
...
[Component that calls swipeAction()]
...
)
}
export function ParentComponent({navigation}) {
...
return (
...
<ChildComponent navigation={navigation}>
...
)
}
Oddly, this works as expected when I run my app on Expo Go on my iOS phone, but it doesn't work when running locally on web. On web, the logic is definitely getting into logoutHandler (console logs at beginning and end of that function fire), but the navigation.navigate doesn't work. Am I doing something wrong?

Related

console.log in provider not printing

I have an expo app that loads a provider:
export const AppProvider = ({ children }: { children: ReactNode }) => {
console.log('Hello from app provider!!');
const alertsBottomSheetRef = useRef<BottomSheetModal>(null);
const dismissAlert = useCallback(() => {
alertsBottomSheetRef.current?.close();
}, []);
const values = {
alertsBottomSheetRef,
dismissAlert,
};
// >>>>> return <AppContext.Provider value={values}>{children}</AppContext.Provider>;
};
If I load the app with the last line commented, I can see the console.log. I can also see any changes I made to the console.log.
However, When I uncomment that last line, I don't get any console.logs.
Any thoughts why?
What I think why its not working because the AppContext.Provider is a context provider, and it does not log anything to the console. The purpose of a context provider is to provide data to components that are descendants of the provider. It does not render anything to the screen and does not log anything to the console.
The issue was that I started the server with expo start and not expo start --dev-client. console logs now appear as expected.

How to call API inside expo SplashScreen?

I'm new in react native so I can't figure out how to add an API call inside SplashScreen in react -native app. The context - I'm building a react-native app expo, which on app load should send API GET request to the backend to get order data, and based on that data I'm either displaying screen A(delivered) or B(order on it's way). I want to add this API call inside the SplashScreen when app still loads so when app is loaded there is no delay in getting API data and displaying screen A/B.
I have a simple useEffect function to call API like this:
const [data, setData] = useState{[]}
useEffect(() => {
const getData = async () => {
try {
const response = await axios.get(url);
if (response.status.code === 200 ) {
setData (response.data) // to save data in useState
}
} else if (response.status.code != 200) {
throw new Error();
}
} catch (error) {
console.log(error);
}
};
getData();
}, []);
and then in the return:
if (data.order.delivered) {
return <ScreenA />
}
else if (!data.order.delivered) {
return <ScreenB />
else {return <ScreenC />}
The issue is that sometimes if API is slow, then after splash screen app has a white screen, or ScreenC can be seen. How can I call API in the splashscreen while app is loading and have a nicer ux?
you can make a custom hook with simple UseState and put it after you've fetched your data
const [loading, setLoading] = useState(true)
...
useEffect(() => {
const getData = async () => {
try {
const response = await axios.get(url);
if (response.status.code === 200 ) {
setData (response.data)
// When data is ready you can trigger loading to false
setLoading(false)
}
...
and After that, you can use a Simple If statement on top of your app.js file
like this
if (!loaded) {
return <LoadingScreen/>; // whetever page you want to show here ;
}
you can use expo expo-splash-screen to achieve this goal:
call this hook on mount...
import * as SplashScreen from 'expo-splash-screen';
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
// Keep the splash screen visible while we fetch resources
await SplashScreen.preventAutoHideAsync();
// Pre-load fonts, make any API calls you need to do here
await Font.loadAsync(Entypo.font);
// Artificially delay for two seconds to simulate a slow loading
// experience. Please remove this if you copy and paste the code!
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) {
console.warn(e);
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
you can also check expo doc

React Native component not making new query after mount

We're using react-native-web so native and web are in one code base. I have an instance where a user clicks the back button to return to a main page and this should fire a re-query of the backend. We're also using Apollo hooks for queries, useQuery
So far, this works for web but not for native. I tried creating a useEffect hook to check if navigation and specifically navigation.isFocused() like so:
const {
data,
loading: childProfilesLoading,
error: childProfilesError,
refetch: refetchChildProfiles,
} = useQuery(LIST_PROFILES, {
fetchPolicy: 'no-cache',
})
// this method also exists on the previous page
const goBack = () => {
if (history) {
history.goBack()
} else if (navigation) {
navigation.goBack()
}
}
useEffect(() => {
if (navigation?.isFocused()) {
refetchChildProfiles()
}
}, [navigation, refetchChildProfiles])
but this doesn't work. Is there something I'm missing in forcing a refetch on native?

Expo in app purchases implementation issues

I am struggling a little with getting in app purchases working due to lack of examples that show how all the functionality links together
In my app, I have one in app purchase which basically allows the user to unlock some restricted functionality for life.
So at the start I want to check if the user have purchased the item before. (via get history)
If they have I unlock the functionality.
On my signed APK file (android) made in android studio, I have the following issues:
-the purchase never acknowledges (although it does when run via react-native run-android)
-if you press the purchase button twice the error "already connected to app store appears"
-I don't think its getting the purchase history when running from the signed file (although I can print out the result in the console in debug mode)
I am not entirely sure when to call await InAppPurchases.connectAsync(); so this could be one potential source of issues
So this is my code in my "Inner App" . My App component is just the InnerApp wrapped in the provider component from redux. The inner app contains all the navigation stacks so the purchase listener should be global.
e.g.
export default function App (){
...more code
return(
< Provider store={store} >
< InnerApp />
</ Provider >
}
Inner app code
import * as InAppPurchases from 'expo-in-app-purchases';
export default function InnerApp (){
.....some more code
//gets purchase history
const getHistory = async ()=>{
await InAppPurchases.connectAsync();
let found=false
const { responseCode, results } = await InAppPurchases.getPurchaseHistoryAsync();
if (responseCode === InAppPurchases.IAPResponseCode.OK) {
results.forEach(result => {
if (result.acknowledged) {
found =true
// this is just saving to local storage (in case they are using the app offline)
savePurchaseHistory(true)
}else{
savePurchaseHistory(false)
}
});
}
if( found){
//updates a state in the redux store
dispatch(purchaseIAP() )
}else if(responseCode === IAPResponseCode.USER_CANCELED ){
dispatch(removeIAP() )
savePurchaseHistory(false)
}
await InAppPurchases.disconnectAsync();
}
//listens for purchases
const setUpIAP = async() => {
// Set purchase listener
await InAppPurchases.connectAsync();
await InAppPurchases.setPurchaseListener(({ responseCode, results, errorCode }) => {
// Purchase was successful
if (responseCode === InAppPurchases.IAPResponseCode.OK) {
results.forEach(purchase => {
if (!purchase.acknowledged) {
// Process transaction here and unlock content...
dispatch(purchaseIAP() )
// Then when you're done
InAppPurchases.finishTransactionAsync(purchase, false);
}
});
}
// Else find out what went wrong
if (responseCode === InAppPurchases.IAPResponseCode.USER_CANCELED) {
} else if (responseCode === InAppPurchases.IAPResponseCode.DEFERRED) {
console.log('User does not have permissions to buy but requested parental approval (iOS only)');
} else {
console.warn(`Something went wrong with the purchase. Received errorCode ${errorCode}`);
}
});
}
//The in app stuff is called when the component is mounted
useEffect(() => {
setUpIAP()
getHistory()
}, [ ] })
Further in my app I have a button that calls the following function when pressed
const unlockModes = async () => {
try {
const items = Platform.select({
ios: [
'dev.products.all_modes'
],
android: ['all_modes'],
});
await connectAsync();
const products = await InAppPurchases.getProductsAsync(items);
if (products.results.length > 0) {
await InAppPurchases.purchaseItemAsync("all_modes");
}
} catch (err) {
alert("error occured while trying to purchase: " + err);
}
};
In the end I used the React Native IAP library and I couldn't get the expo one to work.
I think the Expo Version currently might just be bust.
Setting useGooglePlayCache will resolve your problem

React-Native OneSignal Android error: OneSignal.init has not been called

I'm trying to use the OneSignal react native package to send push notifications to my users. This is working fine for iOS. However, on Android, every call to the OneSignal library times out. When I run adb logcat I see the following message in my console:
E/OneSignal: OneSignal.init has not been called. Could not get OSPermissionSubscriptionState
According to the documentation (https://documentation.onesignal.com/v5.0/docs/react-native-sdk-setup), this is not a step I should do manually, but is rather performed by the library itself when setting up the subscription. I have written some helper functions for OneSignal that are throwing the error above:
getPermissionSubscriptionState = () => {
return new Promise((resolve, reject) => {
OneSignal.getPermissionSubscriptionState((state) =>{
console.log(state);
resolve(state);
});
});
}
enablePushnotifications = async () => {
OneSignal.registerForPushNotifications();
}
ensureSubscription = async () => {
const status = await this.getPermissionSubscriptionState();
console.log(status);
if(!status.notificationsEnabled && !status.hasPrompted) {
this.enablePushnotifications();
}
return status;
}
In my root component, I invoke the ensureSubscription method like so: await OneSignalHelper.ensureSubscription();. Inside ensureSubscription() I invoke the getPermissionSubscriptionState(), but as soon as I do that the above error is printed.
Any help would be greatly appreciated.