[workaround] Next.jsの_document.js内でcookieの値を参照する
概要と注意点
この記事はNext.js
の_document.js
内でcookieの値を参照するためのやり方を記載したものとなります。
コードは基本的にTypeScriptで書いているので、ファイル名の表記も以降は_document.js
ではなく_document.tsx
と表記している場合もありますが、どちらも同じファイルを想定しています。
わざわざ_document.js
内でcookieの値を参照したくなるケースというのは、それほど多くはないかもしれません。
が、例えば、cookieの値を見て、<head>
タグ内でスクリプトの読み込みのON/OFFを制御したいケースなどがあります。
(例えば、Google Analyticsの有効・無効を切り替えたいとか)
なお、最初に注意点がいくつかあります。
まず、今回ここに書くやり方はworkaround的解決方法となります
それはNext.js側で提供されている機能ではない気がするからです。
(Next.jsの挙動見ていると、これは予め想定された手法なのかが判別つかないから)
そのため、このやり方を使わないで済むならそのほうが良いでしょう。
私自身もっと分かりやすい方法がないのか調べているところです。
続いて、今回実現したいことを達成するための手段としてNext.jsに備わっているgetServerSideProps
の処理を動かす必要があります。
そのためNelifyなどに静的サイトとしてホスティングしている場合、getServerSideProps
は使用できないので、このやり方は使えません。
(代替手段があるのかどうかは調べていません)
注意
先ほども書きましたが、果たしてこれがNext.jsで想定されている動きなのか個人的には確信が持てないので、 Next.js
のバージョンによって挙動が変わる可能性も一応明記しておきます。
今回のコードは version "9.5.5"
で検証しています。
Next.jsの_document.js内でcookieの値を参照する
まずは実際に検証用のコードを書いたので、挙動を確認したい場合はこちらのコードをローカルに落として実行してみてください。
Next.js sample of get cookie value at document.tsx
具体的な、_document.js
内でcookieの値を参照する方法は上のコードの通りで、
- 呼び出し対象となるページ(
pages/index.tsx
とか) でgetServerSideProps
関数を設置する。- 別に内部の処理は何も書かなくて良い。->
getServerSideProps
をページ描画時に一度実行したいので
- 別に内部の処理は何も書かなくて良い。->
-
_document.js
内でctx.req.headers.cookie
からcookieの値を参照する
という過程を踏むことで実現が可能です。
実際に _document.tsx
内でcookieの値を参照している箇所はこちらです。一部抜粋しています。
export default class MyDocument extends Document<Props> {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
let hasCookie: boolean = false;
if (ctx.req && ctx.req.headers.cookie) {
console.log('cookie values: ', ctx.req.headers.cookie);
hasCookie = true;
}
return { ...initialProps, hasCookie };
}
render() {
console.log('hasCookie: ', this.props.hasCookie);
_document.tsx
には getServerSideProps
は実装されていない?ようでしたので、getInitialProps
を使って、一度cookieの値を取得するようにしています。
ただ、このコード、cookieの値が実際に入っていたとしても、必ずctx.req.headers.cookie
に値が入っているとは限りません。
呼び出し先のコードである pages/index.tsx
にて getServerSideProps
が動きますが、こちらをコメントアウトすると、上記の_document.tsx
内ではcookie
が参照できなくなります。
diff --git a/pages/index.tsx b/pages/index.tsx
index 99ea97d..23a6337 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -12,9 +12,11 @@ const IndexPage: React.FC = (): JSX.Element => (
</div>
);
+/*
export async function getServerSideProps() {
console.log('----------> Call getServerSideProps');
return { props: {} };
}
+*/
具体的にはctx.req
配下がundefinedとなります。
どうやら getServerSideProps
が動いたあとだけ、_document.tsx
内でctx.req
内に値が格納されるようです。
このような動きについては、Next.js内のこちらのコードが参考になるかと思います。
(自信はありません。間違えていたらすみません!)
getServerSideProps
が動いたあと、ctx.req
に値が入るという流れです。
そのため現状、一度サーバサイドで動かしたあとにdocument側でそれらの値(今回の場合、ctx.req
)を参照する必要があるように感じました。
もっと良い方法をご存じの方がいらっしゃいましたらコメント頂けますと幸いですm(_ _)m
Discussion
知らなかったです…これはハマりポイントですね。
本筋から外れますが、以前
_document.js
or_app.js
でgetInitialProps
を使おうとしたことがあったのですが、getStaticProps
によりページを静的ファイルとして配信したい場合もgetInitialProps
が動いてしまいレスポンスが遅くなってしまう点がネックとなり断念しました。このあたりのNext.jsのノウハウがGitHubのIssues以外であまり見つからないので、僕もどこかで知っていることを記事にしようと思います。
これ、知りませんでした。たぶん同じ状況になったらハマっていたと思うので、知れて良かったです。
情報共有、ありがとうございます。
楽しみにしています!