react-native-google-mobile-ads
react-native-google-mobile-ads copied to clipboard
Unhandled promise rejection: Error: InterstitialAd.show() The requested InterstitialAd has not loaded and could not be shown
Issue
I display a list of addresses. Each time the user clicks on an address, an ad must be shown. Once the ad is closed, the user is automatically redirected to a sub screen where the address is shown on a map.
I've used the useInterstitialAd
and useRewardedAd
hooks. It always works fine the first time the user clicks on an address.
But when he comes back from the Map
screen to the AddressesList
screen, the ad never shows up again when he clicks on a new address. It's like the ad is only loaded once, and can never be shown again afterwards.
The error is:
Unhandled promise rejection: Error: InterstitialAd.show() The requested InterstitialAd has not loaded and could not be shown
I would like to show an ad each time the user clicks on an address.
Here is the wrapper I've written around these hook:
import { useEffect, useState } from "react";
import { NavigationProp } from "@react-navigation/native";
import { TestIds, useInterstitialAd } from "react-native-google-mobile-ads";
import { Routes } from "routes";
const adsId = __DEV__
? TestIds.INTERSTITIAL
: "ca-app-pub-blabla";
export default function useAds( navigation: NavigationProp<any> ) {
const { isLoaded, isClosed, load, show } = useInterstitialAd(adsId, {
requestNonPersonalizedAdsOnly: true,
});
const [navData, setNavData] = useState<string>("");
useEffect(() => {
load();
}, [load]);
useEffect(() => {
if (isClosed) {
navigation.navigate(Routes.Map, { address: navData.address });
}
}, [address, isClosed, navigation]);
const onPress = async (address: string) => {
if (isLoaded) {
setNavData( address);
return show();
} else {
return navigation.navigate(Routes.Map, { address });
}
};
return {
onPress,
};
}
How to fix this?
Try wrapping onPress with useCallback
.
To me it looks like the ad is actualy just loaded once and thus can only be shown once. Maybe adding something like the following could work?
React.useEffect(() => {
if (isClosed) {
load();
}
}, [isClosed, load]);
Indeed, the ad is only loaded once. The loaded state is never reset after the promise resolves. This should be fixed in a pull request. Another option would be to expose a function that could reset the state programatically.
I eventually juste added the condition isLoaded && isOpened, so I make sure not to load the ad more than once.
@DoneDeal0 I am facing same issue with Rewarded Ads and RewardedInterstitial Ads.
Indeed, the ad is only loaded once. The loaded state is never reset after the promise resolves.
This is intended behavior. You need to load ad again using load
function after the ad is used(opened and closed). If the hook programmatically reloads the ad right after the ad is closed, the states(such as isClosed
) will be reset right after the ad is closed, thus developers cannot handle ad's state after the ad is closed(isClosed
never becomes to true
).
Also, the isOpened
state is remained true
even after the ad is closed unless new ad is requested. It is documented in documentation.
So isOpened
does not means if the ad is opened currently, but means if the ad had been opened.
If you want to load ad again, you can do like this:
useEffect(() => {
if (isClosed) {
navigation.navigate(Routes.Map, { address: navData.address });
load();
}
}, [navData.address, isClosed, load, navigation]);
Facing the same issue here...
It seems ad objects cannot be reused (i.e. re-loaded): https://github.com/invertase/react-native-google-mobile-ads/blob/5d2048b530b798740c9fed392772568f909dd6b2/src/ads/MobileAd.ts#L176
Even if the state within the hook resets, the ad object itself won't load more than once, so you need to create a new ad instance...
Facing the same issue here...
It seems ad objects cannot be reused (i.e. re-loaded): https://github.com/invertase/react-native-google-mobile-ads/blob/5d2048b530b798740c9fed392772568f909dd6b2/src/ads/MobileAd.ts#L176
Even if the state within the hook resets, the ad object itself won't load more than once, so you need to create a new ad instance...
Please, try my solution I commented above. The hook won't reload ad for you. You have to load the ad again manually after the ad is used.
Also, the line you mentioned doesn't matter because of these lines: https://github.com/invertase/react-native-google-mobile-ads/blob/5d2048b530b798740c9fed392772568f909dd6b2/src/ads/MobileAd.ts#L98-99
@wjaykim I can reload the ads manually after the previous ad is used, But I can't relead the new ad if the previous one is an error? Do you have any suggestion, thanks
Hi @wjaykim, Thanks a lot for your reply. Indeed your solution works when triggering the load
, show
functions imperatively (e.g. via Button
component). However, when passing them within a callback the behavior is not as expected and the ads won't show neither load. To illustrate it, below is an example of what I'm trying to achieve. The ads are shown and reload properly when using the buttons. However, they don't show up when the app comes back from the background, which would be the expected behavior as part of the handleAppStateChange
.
import React, { useEffect, useRef } from 'react';
import {AppState, StyleSheet, StatusBar, View, Button} from "react-native";
import { MobileAds, useInterstitialAd, TestIds } from 'react-native-google-mobile-ads';
const adUnit = TestIds.INTERSTITIAL;
const requestOptions = {};
export default function App() {
const { isLoaded, isClosed, load, show } = useInterstitialAd(adUnit, requestOptions)
const appState = useRef(AppState.currentState);
const handleAppStateChange = async (nextAppState) => {
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground! Showing ad...');
show();
console.log('Ad should have been shown.');
}
else if (nextAppState === 'background') {
console.log('App is going to the background')
}
else {
console.log('inactive-state transition (iOS)')
}
appState.current = nextAppState;
};
initApp = async () => {
StatusBar.setBarStyle('light-content');
await MobileAds().initialize();
load();
}
useEffect(() => {
initApp();
const sub = AppState.addEventListener('change', handleAppStateChange);
return () => {sub.remove();}
}, []);
useEffect(() => {
if (isClosed) {
console.log("Reloading ad...");
load();
}
}, [isClosed])
return (
<View
style={styles.app}
>
<View style={styles.container}>
<Button
title="Show Ad!"
color="#0ff233"
onPress={show}
/>
<Button
title="Load Ad!"
color="#0ff233"
onPress={load}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
app: {
flex: 1,
backgroundColor: '#646464',
},
container: {
flex: 1,
justifyContent: 'center',
marginHorizontal: 16,
},
});
I could solve it by making the effect listen to changes in the show
function. It seems the show
, load
functions are instantiated more than once... This way we rebind the event handler whenever the show
,load
functions change.
export default function App() {
const { isLoaded, isClosed, load, show } = useInterstitialAd(adUnit, requestOptions)
...
useEffect(() => {
initApp();
}, []);
useEffect(() => {
const sub = AppState.addEventListener('change', handleAppStateChange);
return () => {sub.remove();}
}, [show]);
...
@monti-python Always follow hook rules of react. You can lessen the human errors like those case by using eslint-plugin-react-hooks(The plugin is included in react native's default eslint rule).
Also, if you want to display ad when user get back to foreground, you can use app open ad type, FYI.
There is no need at all to mess-up with the state to reload interstitials, you can do just
if (interstitial.loaded) await interstitial.show();
else {
// It might be a good idea to show a fallback ad here,
// e.g. in my case if no AdMob interstitial ad is loaded,
// I fallback to show a dummy full screen ad, inviting users
// to subscribe for ads-free use of my app.
}
// In my code there is always 1 sec interval between ".show()" and the next line,
// but probably no need for extra wait here, and then no need for "if" on the next line.
if (!interstitial.loaded) interstitial.load();
There is no need at all to mess-up with the state to reload interstitials, you can do just
if (interstitial.loaded) await interstitial.show(); else { // It might be a good idea to show a fallback ad here, // e.g. in my case if no AdMob interstitial ad is loaded, // I fallback to show a dummy full screen ad, inviting users // to subscribe for ads-free use of my app. } // In my code there is always 1 sec interval between ".show()" and the next line, // but probably no need for extra wait here, and then no need for "if" on the next line. if (!interstitial.loaded) interstitial.load();
I didn't know that loaded
is a method that we can use. I really wish this had been in the readme documentation. Would have saved us a lot of problems! Thanks so much for sharing!
Hmm... no idea how do I know about loaded
prop. Probably it was in older docs, got lost on the way, and should be re-added to the docs? @mikehardy
Though, as of the current implementation, that prop is available for all ads that inherit to MobileAd
, thus these classes have it:
- AppOpenAd
- InterstitialAd
- RewardedAd
- RewardedInterstitialAd
I'm traveling so this is really delayed, but I actually looked at it a few days ago when it first went through. I actually had trouble locating it in the source! So, it's hard to find. And near as I can tell it is not in our example either. @wjaykim is loaded
property of MobileAd super class an intended way to program this stuff? :thinking: if so a PR to the docs + example showing it might be useful
Had a chance to roll through some release mode issues and I can verify that in android on release mode at least (maybe other cases), the ad events are for some reason not coming through to the listener, which is related to this.
I've added some code to the example in a PR #244
Hi, since i stumbled across the same error message and i had also troubles finding a solution, i share my working one - maybe somebody can use it:
const showAd = () => {
const rewarded = RewardedAd.createForAdRequest(adUnitId, {
requestNonPersonalizedAdsOnly: true,
keywords: ['fashion', 'clothing'],
});
const unsubscribeLoaded = rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => {
rewarded.show();
});
const unsubscribeEarned = rewarded.addAdEventListener(
RewardedAdEventType.EARNED_REWARD,
reward => {
console.log("Callback called!");
},
);
rewarded.load();
}
Has anyone found a solution? For me, the ad never loads and therefore never displays.
Has anyone found a solution? For me, the ad never loads and therefore never displays.
@victorroatt the related underlying code has changed since this issue was reported. I would recommend filling a new issue with all the relevant information (especially relevant code snippets and the library version you're using) or at least provide that information here.
Hello 👋, to help manage issues we automatically close stale issues.
This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?
This issue will be closed in 15 days if no further activity occurs.
Thank you for your contributions.
Anyone can please share the code for showing multiple ads with the click of a button I am having the same issue first ad lodes perfectly but other ads not loading and while calling shows my app is crashing
+1
Hi, since i stumbled across the same error message and i had also troubles finding a solution, i share my working one - maybe somebody can use it:
const showAd = () => { const rewarded = RewardedAd.createForAdRequest(adUnitId, { requestNonPersonalizedAdsOnly: true, keywords: ['fashion', 'clothing'], }); const unsubscribeLoaded = rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => { rewarded.show(); }); const unsubscribeEarned = rewarded.addAdEventListener( RewardedAdEventType.EARNED_REWARD, reward => { console.log("Callback called!"); }, ); rewarded.load(); }
Worked, Thanks a lot :)
Worked, Thanks a lot
import React, { useEffect } from "react"; import { View, Text, SafeAreaView, StyleSheet, TouchableOpacity, } from "react-native";
import admob, { MaxAdContentRating, } from "react-native-google-mobile-ads";
import { InterstitialAd, RewardedAd, RewardedAdEventType, TestIds, AdEventType, BannerAd, BannerAdSize,
} from "react-native-google-mobile-ads";
admob() .setRequestConfiguration({ // Update all future requests suitable for parental guidance maxAdContentRating: MaxAdContentRating.PG,
// Indicates that you want your content treated as child-directed for purposes of COPPA.
tagForChildDirectedTreatment: true,
// Indicates that you want the ad request to be handled in a
// manner suitable for users under the age of consent.
tagForUnderAgeOfConsent: true,
}) .then(() => { // Request config successfully set! });
const interstialAdUnitId = DEV ? TestIds.INTERSTITIAL : "ca-app-pub-8363970344320746/2547838027";
const interstitial = InterstitialAd.createForAdRequest( interstialAdUnitId, { requestNonPersonalizedAdsOnly: true, keywords: ["fashion", "clothing"], } );
const rewardedAdUnitId = DEV ? TestIds.REWARDED : "ca-app-pub-8363970344320746/7062917668";
const rewarded = RewardedAd.createForAdRequest( rewardedAdUnitId, { requestNonPersonalizedAdsOnly: true, keywords: ["fashion", "clothing"], } );
const bannerAdUnitId = DEV ? TestIds.BANNER : "ca-app-pub-8363970344320746/9472339023";
const App = () => { useEffect(() => { // Event listener for interstitial Ads const interstitialAdEventListener = () => { console.log("Interstitial Ad Loaded"); };
// Start loading the interstitial straight away
interstitial.load();
// Add event listener for interstitial ad loaded
interstitial.addAdEventListener(AdEventType.LOADED, interstitialAdEventListener);
// Event listener for rewarded Ads
const rewardedAdEventListener = (type, error, reward) => {
if (type === RewardedAdEventType.LOADED) {
console.log("Rewarded Ad Loaded");
}
if (type === RewardedAdEventType.EARNED_REWARD) {
console.log("User earned reward of ", reward);
}
};
// Start loading the rewarded ad straight away
rewarded.load();
// Add event listener for rewarded ad loaded and reward earned
rewarded.addAdEventListener(RewardedAdEventType.LOADED, rewardedAdEventListener);
rewarded.addAdEventListener(RewardedAdEventType.EARNED_REWARD, rewardedAdEventListener);
// Unsubscribe from events on unmount
return () => {
interstitial.removeEventListener(AdEventType.LOADED, interstitialAdEventListener);
rewarded.removeEventListener(RewardedAdEventType.LOADED, rewardedAdEventListener);
rewarded.removeEventListener(RewardedAdEventType.EARNED_REWARD, rewardedAdEventListener);
};
}, []);
return ( <SafeAreaView style={styles.container}> <Text style={styles.heading}> React Native + AdMob Integration{"\n\n"} www.aboutreact.com </Text> <View style={{ flex: 1, alignItems: "center", justifyContent: "center", }} > <TouchableOpacity style={styles.buttonStyle} activeOpacity={0.5} onPress={() => interstitial.show()} > <Text style={styles.buttonTextStyle}> Show Interstitial Ad </Text> </TouchableOpacity> <TouchableOpacity style={styles.buttonStyle} activeOpacity={0.5} onPress={() => rewarded.show()} > <Text style={styles.buttonTextStyle}> Show Rewarded Ad </Text> </TouchableOpacity> </View> <BannerAd unitId={bannerAdUnitId} size={BannerAdSize.ANCHORED_ADAPTIVE_BANNER} requestOptions={{ requestNonPersonalizedAdsOnly: true, }} /> </SafeAreaView> ); };
export default App;
const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "white", padding: 16, }, heading: { fontSize: 20, textAlign: "center", marginTop: 30, }, buttonStyle: { minWidth: 300, backgroundColor: "#7DE24E", borderWidth: 0, color: "#FFFFFF", borderColor: "#7DE24E", height: 40, alignItems: "center", borderRadius: 30, marginLeft: 35, marginRight: 35, marginTop: 20, marginBottom: 25, }, buttonTextStyle: { color: "#FFFFFF", paddingVertical: 10, fontSize: 16, }, });
Same issue iam facing iam unable to load the ads and its showing Error: InterstitialAd.show() The requested InterstitialAd has not loaded and could not be shown., js engine: hermes
Please try my solution. It is working
import {View, StyleSheet, TouchableOpacity, Button} from 'react-native';
import {useEffect, useState} from 'react';
import {
InterstitialAd,
TestIds,
AdEventType,
} from 'react-native-google-mobile-ads';
const TestAds = props => {
const [isAdViewHidden, setIsAdViewHidden] = useState(false);
useEffect(() => {
setIsAdViewHidden(false);
}, [isAdViewHidden]);
return (
<View style={styles.container}>
{!isAdViewHidden && (
<InterstitialAdComponent
onShowCallback={() => setIsAdViewHidden(true)}
/>
)}
</View>
);
};
const InterstitialAdComponent = ({onShowCallback}) => {
const adUnitId = TestIds.INTERSTITIAL;
const interstitial = InterstitialAd.createForAdRequest(adUnitId, {
keywords: ['fashion', 'clothing'],
});
useEffect(() => {
const unsubscribe = interstitial.addAdEventListener(
AdEventType.LOADED,
() => {
console.log('Loaded');
},
);
// Start loading the interstitial straight away
interstitial.load();
// Unsubscribe from events on unmount
return unsubscribe;
}, []);
useEffect(() => {
const unsubscribe = interstitial.addAdEventListener(
AdEventType.CLOSED,
() => {
console.log('closed');
onShowCallback();
},
);
// Unsubscribe from events on unmount
return unsubscribe;
}, []);
return (
<View>
<Button
title="Show Interstitial"
onPress={() => {
try {
interstitial.show();
} catch (error) {
console.log(error);
}
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingVertical: 200,
},
});
export default TestAds;