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

如何在React 18的useEffect自动重挂载特性下解决Google AdSense报错

解决React 18严格模式下Next.js集成Google AdSense的重复push报错

在React 18严格模式的开发环境中,组件会被模拟卸载后重新挂载,导致AdSense的adsbygoogle.push({})被重复调用,触发报错:

"adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them."

你的组件代码如下:

const GoogleAdSenseContainer = ({
  client = AdSense_ID,
  slot,
  className,
  responsive = true,
  displayAdSize = displayAdSizes.responsive,
  adUnitKey,
}: Props) => {
  const router = useRouter();
  const loadAds = () => {
    try {
      window.adsbygoogle = window.adsbygoogle || [];
      window.adsbygoogle.push({});
    } catch (error) {
      console.error('🚨 AdSense Error', error);
    }
  };

  useEffect(() => {
    loadAds();
  }, [adUnitKey, router]);

  return (
    <div
      className={cx(styles.adSenseContainer, className)}
      style={{ overflow: 'hidden' }}
      key={adUnitKey}
    >
      <span className={styles.adLabel}>Advertisement</span>
      <ins
        className='adsbygoogle'
        style={displayAdSize}
        data-ad-client={client}
        data-ad-slot={slot}
        data-ad-format={responsive && 'auto'}
        data-full-width-responsive={responsive}
      ></ins>
    </div>
  );
};

以下是几种可行的解决方案:

方案一:检查当前ins元素是否已加载广告

通过ref获取组件内的ins元素,判断它是否已经被AdSense处理过,避免重复调用push:

const GoogleAdSenseContainer = ({
  client = AdSense_ID,
  slot,
  className,
  responsive = true,
  displayAdSize = displayAdSizes.responsive,
  adUnitKey,
}: Props) => {
  const router = useRouter();
  const insRef = useRef<HTMLElement>(null);

  const loadAds = () => {
    try {
      if (!insRef.current) return;
      
      // 判断广告是否已加载:检查是否有子元素或data-ad-status属性
      const hasAd = insRef.current.children.length > 0 || insRef.current.getAttribute('data-ad-status');
      if (hasAd) return;

      window.adsbygoogle = window.adsbygoogle || [];
      window.adsbygoogle.push({});
    } catch (error) {
      console.error('🚨 AdSense Error', error);
    }
  };

  useEffect(() => {
    loadAds();
    
    // 组件卸载时清理广告元素状态
    return () => {
      if (insRef.current) {
        insRef.current.removeAttribute('data-ad-status');
        while (insRef.current.firstChild) {
          insRef.current.removeChild(insRef.current.firstChild);
        }
      }
    };
  }, [adUnitKey, router]);

  return (
    <div
      className={cx(styles.adSenseContainer, className)}
      style={{ overflow: 'hidden' }}
      key={adUnitKey}
    >
      <span className={styles.adLabel}>Advertisement</span>
      <ins
        ref={insRef}
        className='adsbygoogle'
        style={displayAdSize}
        data-ad-client={client}
        data-ad-slot={slot}
        data-ad-format={responsive && 'auto'}
        data-full-width-responsive={responsive}
      ></ins>
    </div>
  );
};

方案二:用全局集合跟踪已加载的广告单元

维护一个全局Set记录已处理的adUnitKey,避免对同一个广告单元重复调用push:

// 全局变量,记录已加载的广告单元key
const loadedAdUnits = new Set<string>();

const GoogleAdSenseContainer = ({
  client = AdSense_ID,
  slot,
  className,
  responsive = true,
  displayAdSize = displayAdSizes.responsive,
  adUnitKey,
}: Props) => {
  const router = useRouter();

  const loadAds = () => {
    try {
      if (!adUnitKey || loadedAdUnits.has(adUnitKey)) return;
      
      window.adsbygoogle = window.adsbygoogle || [];
      window.adsbygoogle.push({});
      loadedAdUnits.add(adUnitKey);
    } catch (error) {
      console.error('🚨 AdSense Error', error);
    }
  };

  useEffect(() => {
    loadAds();
    
    // 组件卸载时移除记录,确保严格模式下重新挂载能正常加载
    return () => {
      if (adUnitKey) {
        loadedAdUnits.delete(adUnitKey);
      }
    };
  }, [adUnitKey, router]);

  // 组件其余部分保持不变
};

方案三:开发环境临时禁用React严格模式

如果只是开发调试时受报错影响,可以在next.config.js中关闭严格模式:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
  // 其他配置项...
};

module.exports = nextConfig;

注意:生产环境建议开启严格模式,此方案仅用于开发调试。

方案四:延迟调用广告加载逻辑

通过延迟执行loadAds,避开严格模式下的重复挂载触发:

useEffect(() => {
  const timer = setTimeout(() => {
    loadAds();
  }, 100); // 延迟100ms执行
  
  return () => clearTimeout(timer);
}, [adUnitKey, router]);

内容的提问来源于stack exchange,提问作者John E

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:41:39