例えば Next.js でこんなコンポーネントを表示しようとしても、
const Component = () => {
return <p>This is {window.location.href}</p>;
};
ReferenceError: window is not defined
になります。
これを回避するには、useEffect
の中で window
オブジェクトにアクセスする必要があります。回避と書きましたがこれが正しい対応方法です。
でも、そもそもこの部分は CSR 前提で作っているから SSR は使わないのよ、と言う場合は SSR を無効化することもできます。
next/dynamic の Dynamic Import を使いましょう #
こんな感じでコンポーネントが export
されているとします。
components/index.tsx
const Component = () => {
return <p>This is {window.location.href}</p>;
};
export { Component };
変更前 #
それを index
ページが import
して表示しているとします。このままだと先程のエラーが出てしまいます。
pages/index.tsx
import { Component } from "src/components";
export default Component;
変更後 #
では index
ページを SSR しないように設定します。それが以下です。
pages/index.tsx
import dynamic from "next/dynamic";
export default dynamic(
async () => {
const module = await import("src/components");
return module.Component;
},
{ ssr: false },
);
こうすると dynamic import されたコンポーネントは必ず CSR でのみ実行されます。
useEffect
を挟まずとも window
オブジェクトにアクセス可能です。他には Next.js の router.query
が初回に undefined
を返すこともなくなります。一発でクエリストリングが取得できます。
dynamic import はコンポーネント単位で行えるため、ページの一部分のみを dynamic import することでその部分だけを SSR 無効化させることも可能です。
アプリ全体を CSR 化してしまうなら #
アプリ全体を一括で CRS 化するのであれば、CSR 用の高階コンポーネントを作成して _app.tsx
で他のコンポーネントを囲ってしまうのが楽です。
components/CSRInner.tsx
const CSRInner = ({ children }: { children: JSX.Element | JSX.Element[] }) => {
return <>{children}</>;
};
export default CSRInner;
components/CSR.tsx
import dynamic from "next/dynamic";
const CSR = dynamic(() => import("./CSRInner"), { ssr: false });
export default CSR;
pages/_app.tsx
import CSR from "src/components/CSR";
import type { AppProps } from "next/app";
const App = ({ Component, pageProps }: AppProps) => {
return (
<CSR>
<Component {...pageProps} />
</CSR>
);
};
export default App;
リファレンス #
公式のわかりやすいドキュメントがありますので、後の詳細はそちらをぜひご参照ください。