Next.jsでCSRアプリを実装する

Next.jsを使ってCSRアプリを実装する方法について書きます。自分が開発をしていた時にCSR化の情報があまり得られなかったのでここに書いておきます。

昨今SSG(静的サイトジェネレート)が追加されてから今はこちらを利用するようにVercelは推奨しています。
しかしながら、実装を進めていく際、ホットリロードする時もSSR(サーバサイドレンダリング)環境で進めることの方が多いのではないでしょうか?

自分自身、SSR環境で実装を行うと、しばし予期せぬwarningに見舞われることがありました。
例えば、クライアントとサーバーでのレンダリング内容に差異が生じしてしまう
Warning: Text content did not match. Server: "foo" Client: "foo".のようなエラー。
(クライアントとサーバー間のタイムスタンプが異ってしまい、サーバとクライアントが完全に一致しないエラー。クライアントとサーバーのレンダリング内容が異なってしまっている時に出ます)

今回はこのSSRをさせないようにしてCSR(クライアントサイドレンダリング)のみを再現させる方法について書きます。

実装方法には2つのパターンが存在しています。

パターン1

まず一つ目。typof windowを使用してクライアントかサーバーかを判定する。
_app.tsxについての説明はここでは詳しく書きませんが、レンダリングするコンポーネントはここを必ず通ります。
そこでレンダリングする時にCSRなのかSSRなのかを判定して、CSR時のみレンダリングさせるという方法です。
さらに、suppressHydrationWarningを使用してサーバーで起きたエラーは非表示にするという方法。

import React from 'react';
import { AppProps } from 'next/app';

const SafeHydrate = ({ children }) => {
  return (
    <div suppressHydrationWarning>
      {typeof window === 'undefined' ? null : children}
    </div>
  )
};

const App = ({ Component, pageProps }: AppProps) => {
  return (
        <SafeHydrate>
          <Component {...pageProps} />
        </SafeHydrate>
  );
};

export default App;

このsuppressHydrationWarning をtrueにするとクライアントとサーバーで差異があっても警告を出さないようにすることができます。
しかし個人的にはこのsuppressHydrationWarningを使用するのは避けた方がよいと考えていて、dynamic importを使用する方を推奨したいです。

パターン2

dynamic importはNext.jsが提供するAPIの一つで、呼び出したコンポーネントをSPAにして呼び出します。
パターン1同様にコンポーネントは_app.tsxで呼び出します
dynamic importを使用したい場合はコンポーネントを別ファイルに切り出す必要があります。そのためSafeHydrateコンポーネントは別のファイルとして作ります。

import React from 'react';

interface SafeHydrateProps {
  children: JSX.Element | JSX.Element[];
}
const SafeHydrate = (props: SafeHydrateProps) => {
  return <>{props.children}</>;
};

export default SafeHydrate;

これであとはSafeHydrateコンポーネントをdynamic importしてNextの<Component />を子として呼び出せばSPA化できるようになります。

_app.tsx

import React from 'react';
import { AppProps } from 'next/app';
import dynamic from 'next/dynamic';

const App = ({ Component, pageProps }: AppProps) => {

  const SafeHydrate = dynamic(() => import('../components/SafeHydrate'), { ssr: false });

  return (
        <SafeHydrate>
          <Component {...pageProps} />
        </SafeHydrate>
  );
};

export default App;

まとめ

どうだったでしょうか?Next.jsのSPA化について書いてみました。
他にも良い方法があれば教えてもらえたら嬉しい限りです。今日はここまで。

参考
How to Disable Server-Side Rendering (SSR) in Next.js

関連記事