返回重进页面时目标useEffect未执行的问题排查与解决
问题解决:返回后重新进入页面时广告加载逻辑不执行
问题根源
- 插屏广告实例
interstitial在组件外部创建为单例,页面销毁后实例状态残留,重新进入时无法正常初始化加载 - 依赖
navigation的普通useEffect不会在页面重新聚焦时触发(navigation对象未发生变化) - 未将页面聚焦状态与广告加载逻辑绑定,导致返回后进入页面时广告加载逻辑不执行
修复后的完整代码
import { useRoute, useIsFocused, useFocusEffect } from '@react-navigation/native' import React, { useEffect, useState, useRef } from 'react'; import { View, Text, BackHandler } from 'react-native'; import { InterstitialAd, AdEventType, TestIds } from 'react-native-google-mobile-ads'; import { incrementCounter, resetCounter } from '../store/counter'; import { useSelector, useDispatch } from 'react-redux'; import NetInfo from '@react-native-community/netinfo'; import { gql, useQuery } from '@apollo/client'; const QUERY = gql ` //some query ` function AdScreen({navigation}) { const [isConnected, setIsConnected] = useState(true); const [loaded, setLoaded] = useState(false); const isFocused = useIsFocused(); const dispatch = useDispatch(); const counter = useSelector(state => state.counter.count); const interstitialRef = useRef(null); // 用ref保存广告实例 const { params: { title, id }, } = useRoute(); const {data, loading, error, refetch} = useQuery(QUERY, { variables: {id}, fetchPolicy: 'cache-and-network', }); useEffect(() => { const unsubscribe = NetInfo.addEventListener(state => { setIsConnected(state.isConnected); }); return () => unsubscribe(); }, []); const handleRefresh = () => refetch(); // 替换原THIS SECTION的逻辑,改用useFocusEffect useFocusEffect( React.useCallback(() => { // 初始化广告实例 interstitialRef.current = InterstitialAd.createForAdRequest(TestIds.INTERSTITIAL); const unsubscribe = interstitialRef.current.addAdEventListener( AdEventType.LOADED, () => setLoaded(true) ); // 加载广告 interstitialRef.current.load(); // 页面失焦时清理 return () => { unsubscribe(); // 销毁广告实例避免内存泄漏 if (interstitialRef.current) { interstitialRef.current.destroy(); } }; }, []) ); useEffect(() => { const backHandler = BackHandler.addEventListener('hardwareBackPress', handleBackPress); return () => backHandler.remove(); }, []); useEffect(() => { // 计数达标且广告已加载时展示 if (counter >= 5 && loaded && interstitialRef.current) { interstitialRef.current.show(); setLoaded(false); // 展示后重新加载下一次广告 interstitialRef.current.load(); dispatch(resetCounter()); } }, [counter, loaded]); const handleBackPress = () => { dispatch(incrementCounter()); return true; }; return ( <View style={{flex:1, justifyContent:'center', alignItems:'center'}}> <Text> 计数:{counter} | 广告加载状态:{loaded ? '已加载' : '未加载'} | 页面聚焦状态:{isFocused ? '已聚焦' : '未聚焦'} </Text> </View> ) } export default AdScreen
关键改动说明
- 广告实例管理:用
useRef在组件内部保存广告实例,每次页面聚焦时重新创建,避免单例状态残留 - useFocusEffect替代useEffect:确保每次页面进入(包括返回后)都执行广告初始化和加载逻辑,
useFocusEffect会在页面聚焦时触发,失焦时执行清理 - 实例销毁:在页面失焦时调用
destroy()销毁广告实例,避免内存泄漏 - 逻辑简化:移除不必要的
isFocused依赖,广告展示逻辑仅依赖计数和加载状态
内容的提问来源于stack exchange,提问作者Mynameiswha
相关产品推荐
相关产品推荐

