概要
Next.jsでコードを書いている時にこのようなエラーをみたことがあるのではないでしょうか
Warning XXX did not match. Server: XXX Client XXX
このエラーはサーバーとクライアントでレンダリング結果が異なる場合に起こります。Next.jsはビルド時(SSGの本番環境)またはリクエスト時(SSGの開発環境、SSR)にサーバー側で事前にレンダリング(Pre-rendering)が行われサーバ側でコードが走ります、その後、クライアントで処理が走ります。その時の処理のレンダリング結果が異なる場合には上記のエラーになります
この場合、サーバーとクライアントのレンダリング結果を同じにすればいいのですが、どうしようもない場合があります。その時は、Dynamic Import を使ってSSRを回避してCSRにする方法があります
それでは、現在時刻を取得するプログラムを作成し、このエラーをわざと発生させ説明していきます
本題
現在時刻を取得するプログラム
import { useState } from 'react';
const index = () => {
const now = new Date();
const [hour, setHour] = useState(now.getHours());
const [min, setMin] = useState(now.getMinutes());
const [sec, setSec] = useState(now.getSeconds());
setInterval(() => {
const now = new Date();
setHour(now.getHours());
setMin(now.getMinutes());
setSec(now.getSeconds());
}, 1000);
return (
<div>
{hour}時{min}分{sec}秒
</div>
);
};
export default index;
エラー画面
このように毎回エラーが走るわけではありません。サーバーとクライアントのレンダリングが同じ場合も考えられます(秒数が同じ)
最初に起きたエラーのログは
Warning Text content did not match. Server: 30 Client 31
これは、30秒の時にサーバー側でレンダリングされ31秒の時にクライアント側でレンダリングされたため起こったエラーです。実際に30秒の時にリロードしていることがわかると思います
このプログラムを今から書き換えていきます
エラーを修正したプログラム
Dynamic Import
Dynamic Importを使ってSSR回避しCSRにするプログラムです
import dynamic from 'next/dynamic';
const index = () => {
const DynamicComponentWithNoSSR = dynamic(() => import('../components/Timer'), {
ssr: false,
});
return <DynamicComponentWithNoSSR />;
};
export default index;
import { useState } from 'react';
const Timer = () => {
const now = new Date();
const [hour, setHour] = useState(now.getHours());
const [min, setMin] = useState(now.getMinutes());
const [sec, setSec] = useState(now.getSeconds());
setInterval(() => {
const now = new Date();
setHour(now.getHours());
setMin(now.getMinutes());
setSec(now.getSeconds());
}, 1000);
return (
<div>
{hour}時{min}分{sec}秒
</div>
);
};
export default Timer;
これで、何度リロードを押してもエラーが走らないのが確認できると思います
これでTimer.jsx
内の処理はクライアントだけの処理になっています
修正後デモ画面
おわりに
何か他の良いやり方知ってる方がいればコメントで教えていただけるとうれしいです