React Native中FlashList内BannerAd加载缓慢及预加载需求
问题解决方案
一、实现BannerAd预加载
1. 利用FlashList可视区域监听提前触发加载
FlashList的onViewableItemsChanged回调可以监听即将进入可视范围的列表项,我们可以借助这个特性提前预加载广告。先封装一个带预加载逻辑的Banner组件:
import React, { useEffect, useRef } from 'react'; import { BannerAd, BannerAdSize, TestIds, AdEventType } from 'react-native-google-mobile-ads'; const PreloadedBanner = ({ unitId, adId }) => { const adRef = useRef(null); const isLoaded = useRef(false); useEffect(() => { // 组件挂载后,若未加载则触发广告请求 if (!isLoaded.current && adRef.current) { adRef.current.loadAd(); isLoaded.current = true; } }, []); const handleAdEvent = (event) => { if (event.type === AdEventType.LOADED) { isLoaded.current = true; } }; return ( <BannerAd ref={adRef} key={adId} unitId={unitId} size={BannerAdSize.ANCHORED_ADAPTIVE_BANNER} onAdEvent={handleAdEvent} /> ); }; export default React.memo(PreloadedBanner);
接着在FlashList中配置可视区域监听,提前预加载后续的广告项:
import React, { useCallback } from 'react'; import { FlashList } from "@shopify/flash-list"; import PreloadedBanner from './PreloadedBanner'; const handleViewableItemsChanged = useCallback(({ viewableItems }) => { const lastVisibleIndex = viewableItems[viewableItems.length - 1]?.index || 0; // 提前预加载后面2个位置内的广告(可根据需求调整预加载范围) const preloadRange = 2; for (let i = lastVisibleIndex + 1; i <= lastVisibleIndex + preloadRange; i++) { const item = postList[i]; if (item?.ad) { // 对应广告组件已挂载的话,会自动执行预加载逻辑 } } }, [postList]); // FlashList配置 <FlashList data={postList} renderItem={(item) => { if (item.item.ad) { return ( <View className="my-2 h-[300px] rounded-xl flex justify-center items-center"> <PreloadedBanner unitId={TestIds.BANNER} adId={item.item.id} /> </View> ); } return <Post item={item.item}/>; }} onViewableItemsChanged={handleViewableItemsChanged} viewabilityConfig={{ itemVisiblePercentThreshold: 50, waitForInteraction: false, }} // 保留原有其他属性 />
2. 维护广告实例池预加载
借助react-native-google-mobile-ads的AdLoader提前创建多个广告实例,渲染时直接复用:
import { AdLoader, BannerAdSize, TestIds } from 'react-native-google-mobile-ads'; import React, { useEffect, useRef, useState } from 'react'; const adPool = useRef([]); // 初始化时预加载广告实例 useEffect(() => { const preloadAds = async () => { const loader = await AdLoader.createForBannerAd({ adUnitId: TestIds.BANNER, adSize: BannerAdSize.ANCHORED_ADAPTIVE_BANNER, }); // 预加载3个广告备用 for (let i = 0; i < 3; i++) { loader.loadAd().then(ad => adPool.current.push(ad)); } }; preloadAds(); }, []); // 封装复用实例的组件 const PooledBanner = ({ adId }) => { const [adInstance, setAdInstance] = useState(null); useEffect(() => { if (adPool.current.length > 0) { // 从池中取实例,同时补充新实例 setAdInstance(adPool.current.shift()); const loader = AdLoader.createForBannerAd({ adUnitId: TestIds.BANNER, adSize: BannerAdSize.ANCHORED_ADAPTIVE_BANNER, }); loader.loadAd().then(newAd => adPool.current.push(newAd)); } }, []); if (!adInstance) { return <View className="h-[300px] flex justify-center items-center">加载中...</View>; } return ( <BannerAd key={adId} unitId={TestIds.BANNER} size={BannerAdSize.ANCHORED_ADAPTIVE_BANNER} adInstance={adInstance} /> ); };
在FlashList的renderItem中使用PooledBanner即可直接复用预加载好的广告实例,避免滚动时等待加载。
二、解决重渲染时广告重新加载的问题
1. 稳定组件渲染状态
- 用
React.memo包裹广告组件,确保只有props变化时才重新渲染; - 保证广告项的
key使用唯一且稳定的标识(比如你已用的item.item.id),避免组件被卸载后重新挂载。
2. 缓存广告加载状态
在广告组件内用useRef标记广告是否已加载,即使组件重渲染,ref中的状态不会丢失,不会重复触发加载请求(参考前面PreloadedBanner中的isLoaded实现)。
3. 避免列表数据不必要的更新
检查postList是否每次重渲染都被重新创建,若存在这种情况,用useMemo包裹列表数据,保证只有内容真正变化时才触发FlashList更新:
const stablePostList = useMemo(() => postList, [postList]); // FlashList的data改用stablePostList <FlashList data={stablePostList} ... />
内容的提问来源于stack exchange,提问作者KyleReemoN-
相关产品推荐
相关产品推荐

