はじめに
下記の要件を満たしたく、その方法を書きます。
- Github Pagesを使う
- 無料の静的ホスティングサービスを探して
- ページ遷移がある
- GAでページごとのカウントを取りたい
課題の発生(認識)と、やっつけていった経緯
課題1:BrowserRouterだと404エラー
まず、ページ遷移をreact-router-domのBrowserRouterで実装してGithub Pagesに置いたところ、https://sample.github.io/repo1/page1/にアクセスすると、Page Not Found(404)が出てしまいました🔍
原因は、BrowserRouterはhttps://sample.github.io/repo1/page1/index.htmlをサーバーに問い合わせてしまうから。そしてそれは存在しないから。
課題1の対策
対策として、react-router-domのHashRouterを使いました。その結果URLは、https://sample.github.io/repo1/#/page1/のようになり、そこにアクセスしても、サーバーには問い合わせず表示され、課題は解消しました。
課題2:GAの設定をindex.htmlに入れたがいいのか?
Reactのベースとなるindex.htmlのヘッダーに、GAの初期設定で言われるがまま、gtag.jsをコピペしました。GAにも認識され、よかったよかった。
ちなみに埋め込むgtag.jsと呼ばれるコードは下記。
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-xxxxxxxxxx', {
'anonymize_ip': true,
});
</script>
・・・ところでふと湧いた疑問。SPA(Single Page Application)のindex.htmlに書いたら、Single Page でしかカウントされないのでは?⇒ YES💀
課題2の対策
ページ遷移ごとに、上のコードが動いてもらわないといけないので、HashRouterの中に<PageTracker />を入れて、ページ遷移ごとに強制的にusePageTracking()を動かさせる。
// usePageTrackingを使うための中間コンポーネント
const PageTracker = () => {
usePageTracking();
return null;
};
export default function App() {
return (
<HashRouter>
<PageTracker />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</HashRouter>
)
}
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
declare global {
interface Window {
gtag?: (...args: any[]) => void;
}
}
const usePageTracking = () => {
const location = useLocation();
useEffect(() => {
if (window.gtag) {
const pagePath = location.pathname + location.search;
window.gtag("config", "G-xxxxxxxxxx", {
page_path: pagePath,
anonymize_ip: true,
});
}
}, [location]);
};
export default usePageTracking;
index.htmlだと1回しか呼び出されないけど、こうしておけば、ページ遷移のたびに呼び出されて安心。(とこの時は思った)
課題3:GAでページごとにカウントされていない(バグ)
GAの集計画面を見ていると、どうやらトップページしかカウントされていない。初日だけかと思って数日見てみても、やっぱりそう🤔
GAへ「このページが見られたよー」とgtag()を使って通知する、"このページ"が正しくありませんでした。
react-router-domのuseLocation()の返す値が、BrowserRouterを使う場合とHashRouterで、下記のように変わることがポイント。
// https://example.com/about?ref=twitterへアクセス
location = useLocation();
// location.pathname === "/about"
// location.search === "?ref=twitter"
// location.hash === ""
// https://example.com/#/about?ref=twitterへアクセス
location = useLocation();
// location.pathname === "/"
// location.search === ""
// location.hash === "#/about?ref=twitter"
つまりHashRouterを使うなら、location.hashを読み解いて"/about"を取り出さないといけないんです。
私が課題2の解として書いたlocation.pathname + location.searchは、BrowserRouterならいいけど、HashRouterだとどのページでも、"https://example.com/"になります🤣
課題3の対策
const usePageTracking = () => {
const location = useLocation();
useEffect(() => {
if (window.gtag) {
const pagePath =
location.hash.replace(/^#/, '') // HashRouter 対応
window.gtag("config", "G-xxxxxxxxxx", {
page_path: pagePath,
anonymize_ip: true,
});
}
}, [location]);
};
この結果、location.hashの値"#/about?ref=twitter"は加工され、/about?ref=twitterになり、gtag()へ渡っていくことになります。
なお、?以降の?ref=twitterは、消してはいけません。
私が今回使うかどうかはまだ未定ですが、UTMパラメータというものをURLに入れる可能性があるためです。https://example.com/#/about?utm_source=Googleなどと、どこから来たかという情報も含めてURLを作って、例えばGoogle広告に出すとかすると、Googleから来たということをGAで分析できる ようになります。ここの細かい話は書きませんが、GAで使う場面がよくあるということで、基本的に消してはいけない。
以上の対応で、GAに正しく認識させることができるようになります。
まとめ
Github Pages、React、GAと1つ1つはすごくたくさんの情報があって、単独で問題があっても簡単に解決できますが、それが 組み合わさる と、情報が減るし、微妙に違う問題の解決策を誤って実施してしまうとか変な問題が起きたりしますね。 その点はChatGPTでも同じ で、組み合わせの条件を1つ伝え忘れちゃうと誤った解決策を提示されたりするので、使い方は気を付けないといけません。
課題をクリアするとまた新しい課題が出てきて、課題はいつまで続くのかわからない。それをやっつけ続けて、1つずつ解決してるんだと言い聞かせながら進み、ようやくゴールが見える。
これって抽象度を上げて考えてみると、人生そのものだなと思いました。失敗はない・過程なんだという話、これなんだ。がんばりましょう。
では、よき課題解決ライフを!