如何在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
相关产品推荐
相关产品推荐

