React(Next.js)で開発をしていて、こんなエラーに遭遇しました。
Error: Hydration failed because the initial UI does not match what was rendered on the server.
大した実装をしたわけではなく、jsxのタグを一個追加しただけなのにずいぶん大げさなエラー。
いままで見たことがないものだったので、軽く調べる時間がかかりました。
CSRとSSG/SSRの間に差異がある
Next.jsのドキュメントにはこんな記載がありました。
アプリケーションのレンダリング中に、プリレンダリングされた React ツリー (SSR/SSG) と、ブラウザでの最初のレンダリング時にレンダリングされた React ツリーの間に差異が発生しました。最初のレンダリングは、React の機能である Hydration と呼ばれます。
( https://nextjs.org/docs/messages/react-hydration-error )
CSR(クライアンドサイドレンダリング)とプリレンダリングの内容に違いがあるだと・・?
意味不明でしたがこちらの記事が参考になりました。
ReactDOM.hydrate
は、 サーバサイドで生成された DOM構造と、クライアントサイドで生成された仮想DOMが一致していることを期待している。
一致していれば、DOMのレンダリングをスキップし、イベントリスナーの登録だけを行う。
なるほどReactDOM.hydrateではそんな動作をしているんですね。
修正内容
結論から言えば今回のケースでは以下のような修正により直りました。
pタグの中のdivタグをspanタグに直す
そこまで詳しい仕様は調べていないのですが、おそらくhtmlの構造的に p > div がNext.js / Reactとして想定されていないものだった、ということなのかと思います。
確かに普通はあまりやらないネストですがこの時はやっちゃってました。
今回はこんな簡単な修正で直りましたが、useEffectまわりで同様のエラーにつながる場合があって、その時はもうちょっと複雑な対応が必要みたい。
useEffect
は Hydrate 後に行われるため、useEffect
とuseState
を組合せ、マウントされているかどうかを管理して分岐すると、問題は最小限に抑えられる。
( https://zenn.dev/aiji42/articles/e6f1a798e9b416 )
とりあえず今回はこれまで。
ではまた!