Next.jsのページのレンダリングはデフォルトでPre-rendering、つまりSSR(Server-side Rendering)もしくはSG(Static Generation)です1。
サーバサイドではブラウザ上でのみ実行可能な機能は利用できません。具体例は以下の通りです。
- Windowの参照
- Cookieの参照
- localStrageの参照
- sessionStorageの参照
上記の中でも特にCookieはコンポーネントの制御などでレンダリング時によく利用される機能です。
サーバサイドではCookieを利用できないため、サーバサイドでのレンダリングにCookieによる制御が組み込まれていると意図した結果になりません。
今回はNext.jsにおけるCookieの誤った利用例と改善方法について紹介します。
検証環境のnextは11.1.2を利用しています。
目次
Pre-rendering時にCookieを利用しようとしているダメな例
Cookieの有無によってUIを出し分けるケースを考えます。
以下のコードは『hideMessage
という名前のCookieが存在していなかったらメッセージを表示、存在していたら非表示』という実装の誤った例です。Cookieの操作はjs-cookieで行っています。
import type { NextPage } from "next";
import Cookies from "js-cookie";
const Home: NextPage = () => {
const hideCookie = Cookies.get("hideMessage");
const message = () => {
if (hideCookie != null) {
return null;
} else {
return <div>There is no Cookie</div>;
}
};
return <>{message()}</>;
};
export default Home;
サーバサイドではCookieが取得できないため、hideMessageという名前のCookieが存在する場合でもサーバサイドではCookieが存在していないと認識されます。
以下の画像をみて分かる通り、サーバサイドではCookieが存在しないと判定されているためサーバサイドからのHTMLレスポンスには当該要素の文字列There is no Cookie
が表示されています。
なお、Pre-rendering後に実行されるクライアントサイドのレンダリングではCookieを操作できるため、ブラウザ上の画面にはメッセージが表示されていません。
サーバサイドとクライアントサイドのレンダリング結果が異なるため、Warning: Did not expect server HTML to contain a <div> in <div>.
というReactDOM.hydrate()
の警告がReactのアプリケーションログに表示されています。
レンダリング結果の不一致はパフォーマンス低下やデザイン崩れの原因となるため改善が必要です。
なお、Next.jsのSSRの流れについては【図解】Next.jsのSSRが画面反映されるまでの具体的な流れ、ReactDOM.hydrate()
については【Next.js】Hydration時にReact.hydrate()による警告が発生するケースとその解決方法で紹介していますので、あわせてご覧になってください。
Cookieによる制御をPre-renderingのページで実装する方法
Cookieの有無によって表示するUIを出し分けるのであれば、例えば副作用を活用すると解決できます。
UIを出し分けるフラグをStateに追加し、副作用でCookieの有無に応じてフラグの真偽値を変更させます。
副作用はマウント後に実行されるので、副作用ではCookieの操作ができます。
具体例は以下の通りです。
import type { NextPage } from "next";
import Cookies from "js-cookie";
import { useEffect, useState } from "react";
const Home: NextPage = () => {
const [isHide, setIsHide] = useState(false);
const message = () => {
if (isHide) {
return null;
} else {
return <div>There is no Cookie</div>;
}
};
useEffect(() => {
const hideCookie = Cookies.get("hideMessage");
if (hideCookie != null) {
setIsHide(true);
} else {
setIsHide(false);
}
}, []);
return <>{message()}</>;
};
export default Home;
今回は副作用を利用してCookieを操作する例を紹介しましたが、副作用以外にtypeof window
、process.browser
、dynamic import
を活用する方法もあります。
詳細解説はNext.jsのSSR回避(クライアントサイドでのみ実行)の実装パターン集 で紹介しています。
参考: リロードと画面遷移でページのUIに違いがあった時は要注意
Next.jsのルータ機能を利用するとCSRによる画面遷移となります。Next.jsのルータ機能にはnext/linkとnext/routerがあります。
一方、リロードで画面を表示する場合はPre-rendering(SSR/SG)となります。
なお、next/linkの流れの詳細は【Next.js】next/linkの画面遷移(CSR)から画面反映までの流れの図解解説で解説しています。
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!