You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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-

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:01:48