React18+Next.jsにおけるHydration failedについて
はじめに
React18
+Next.js
で開発をしていたところ初見のエラーに遭遇しました。
エラーの原因が長らくわかりませんでしたが、なんとか解決できたのでその知見を紹介したいと思います。
エラー内容
エラー内容は
Error: Hydration failed because the initial UI does not match what was rendered on the server.
「初期UIがサーバでレンダリングされたものと一致しないためHydration
に失敗しました。」という意味になります。ここでHydration
について考えます。
Hydrationとは
Next.js
では生成されたHTMLが、そのページに必要な最小限のJavaScriptのコードと関連付けられることで、ブラウザがページを読み込んだ際に、そのJavaScriptが実行されページが操作可能(インタラクティブ)になります。この過程をHydration
といいます。
エラーの原因
Hydration
について理解したところで、今回のエラー原因について考えたいと思います。
今回のエラーが生じたコードが以下となります。(エラーの本質部分だけ)
バージョン
- react 18.1.0
- next 12.1.6
import { NextPage } from "next";
import React from "react";
const TopPage: NextPage = () => {
return (
<>
<p>
<div>hello</div>
</p>
</>
);
};
export default TopPage;
HTMLに詳しい人なら当たり前かもしれませんが,pタグ
のなかにdivタグ
があることが問題です。
以下にもあるようにpタグ
内はimgタグ
やbrタグ
といった記述コンテンツしか使用できません。
pタグを用いた理由
pタグ
を用いてしまった理由としてはパフォーマンスを意識しすぎたためです。
以前以下のような記事を読み、divタグ
の多様がパフォーマンスの低下につながることを学んだため、divタグ
をpタグ
に変更する方針で開発していました。
しかしpタグ
の仕様を深く理解していなかったため、divタグ
との互換性がなく誤用してしまいました。
https://zenn.dev/uhyo/articles/usememo-time-cost
React17との比較
私は以前からdivタグ
の代わりにpタグ
を用いていました。
しかしそのときは、Hydration failed
は発生せず開発できていました。
その時のReactのバージョンがReact17
だったのでReact17
とReact18
で比較してみます。
React17
バージョン
- react 17.0.2
- next 12.0.9
import { NextPage } from "next";
import React from "react";
const TopPage: NextPage = () => {
return (
<>
<p>
<div>hello</div>
</p>
</>
);
};
export default TopPage;
先程のReact18
のときとコード内容は同じにしてコンソールパネルで表示に違いがでるか比較しました。以下がReact17
における結果です。
React18
React18
における結果が以下となります。
このことからもわかるように、pタグ
の誤用はReact17
、React18
のどちらも警告止まりであることがわかります。しかしReact18
以降では警告に加えHydration
に関するエラーが出現するようになりました。
解決策
現在の所感では、Hydration falied
のエラーにもあるように不正なHTMLを用いることで、サーバで生成されたHTMLとクライアントで生成されたHTMLに違いがでてしまっているのかなというところです(なぜ違いが出てしまっているのかはわからない)。したがってHydration failed
が生じたときはHTMLのタグの使い方に間違いがないかを確認することが先決です。それでも解決しない場合はReact17
にダウングレードするのがいいかもしれません。
Discussion