Next.jsでGoogle Adsenseを使った広告配信

妙にハマりまくったので、とりあえず現状について覚書

参考

pages/_document.tsx

Google Adsenseのscriptタグをここで読ませます。react-helmetを使えばまとめられる気もしますが、ライブラリ化するわけでもないので今はこれでよしとしています。

      <Html>
        <Head>
          <script
            async
            src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
            data-ad-client="ca-pub-xxxxxxx"
          ></script>
        </Head>

@types/window.d.tsで型を拡張

windowadsbygoogleが追加されるので、型を定義しておきます。

/* eslint-disable no-var */
interface Window {
    prerenderReady: boolean;
    adsbygoogle: {[key: string]: unknown}[]
}
  
declare global {
    var window: Window;
}

コンポーネントを作成

react-adsenseのコードなどを参考にコンポーネントを作ります。

import React, {CSSProperties, FC, useEffect} from 'react'

export type GoogleAdsenseProps = {
  className?: string;
  style?: CSSProperties;
  client: string;
  slot: string;
  layout?: string;
  layoutKey?: string;
  format?: string;
  responsive?: string;
};

export const GoogleAdsense: FC<GoogleAdsenseProps> = ({
  className = '',
  style = {display: 'block'},
  format = 'auto',
  layout = '',
  layoutKey = '',
  responsive = 'false',
  client,
  slot,
}) => (
    <ins className={`${className} adsbygoogle`}
      style={style}
      data-ad-client={client}
      data-ad-slot={slot}
      data-ad-layout={layout}
      data-ad-layout-key={layoutKey}
      data-ad-format={format}
      data-full-width-responsive={responsive}></ins>
);

pages/_app.tsxでイベント発火

あとはwindow.addsbygoogleを実行するフックを追加します。

const useGoogleAdsense = () => {
  const loadAd = useCallback(() => {
    (window.adsbygoogle = window.adsbygoogle || []).push({})
  },[])

  useEffect(() => {
    if(typeof window !== 'undefined'){
      const component = window.document.getElementById('__next').querySelector(`.adsbygoogle`);
      if (component) {
        component.addEventListener('load', loadAd);
      }
    }
    return () => {
      if(typeof window !== 'undefined'){
        const component = window.document.getElementById('__next').querySelector(`.adsbygoogle`);
        if (component) {
          component.removeEventListener('load', loadAd);
        }
      }
    }
  }, [])
}

function MyApp({ Component, pageProps }) {
  useGoogleAdsense()
  return (
            <Component {...pageProps} />
  )
}

Next.jsアプリ配下に広告タグがあれば実行。なければ何もしないというフックにしています。

window.onloadで読み込みしているサンプルが多かったのですが、Next.jsやGatsbyでは使わない方が良さそうです。というのもこの辺りのFWを使った場合、ページ遷移時にwindow.onloadイベントが発火しない様子で、広告が表示されませんでした。

そのため今回のサンプルではちょっと手間ですが、querySelectorで要素を取得し、addEventListnerでloadイベントをセットする形を採用しています。

ページ毎にuseEffectを白化させる

もしページ遷移のたびにHookを動かしたい場合はこうします。

import { useRouter } from 'next/router';
...
  const {asPath} = useRouter()
  useEffect(() => { ... }, [asPath])

ある程度整理できたら、ライブラリ化できないか検討しようかなとは思っています。

Comment