実現したいこと
Next.jsで作っているブログサイトで発生しているHydration Errorを解消したい。
前提
Next.jsでブログサイトを作っています。
記事の内容などはWordpressで作成し、APIで情報を取得することでNext.js側で表示させます。
個別の記事を表示させるページでHydration Errorが発生。
Wordpressから取得した記事本文の内容はhtml-react-parserというライブラリを利用しパースしています。
もしもアフィリエイトというアフィリエイトサイトで作ったJavaScriptを含むリンクが取得したHTMLの中に含まれる際にHyderation Errorが発生してしまいます。
逆にいうとこのリンクが入っていなければHydration Errorは発生しない状況です。
発生している問題・エラーメッセージ
Unhandled Runtime Error Error: Text content does not match server-rendered HTML. See more info here: https://nextjs.org/docs/messages/react-hydration-error
該当のソースコード
以下のコードでimgタグをImageコンポーネントに置き換えるのと、h2・h3タグの前にFontAwesomeのアイコンの差し込みを行っています。
TypeScript
1import parse, { DOMNode, Element, domToReact } from "html-react-parser";2import Image from "next/image";3import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";4import { faFolderOpen } from "@fortawesome/free-regular-svg-icons";5import styles from 'styles/reactParser/reactParser.module.scss'6 7type ContentHTMLProps = {8 contentHTML: string;9};10 11const ConvertBody = ({ contentHTML }: ContentHTMLProps) => {12 const contentReact = parse(contentHTML, {13 replace: (domNode: DOMNode) => {14 if (domNode instanceof Element) {15 if (domNode.name === "h2") {16 return (17 <h2>18 <FontAwesomeIcon icon={faFolderOpen} className={styles.h2icon} />19 {domToReact(domNode.children)}20 </h2>21 );22 }23 24 if (domNode.name === "h3") {25 return (26 <h3>27 ■ 28 {domToReact(domNode.children)}29 </h3>30 );31 }32 33 if (domNode.name === "img") {34 const { attribs } = domNode;35 const { src, alt, width, height } = attribs;36 return (37 <Image38 src={src}39 width={Number(width)}40 height={Number(height)}41 alt={alt}42 sizes="(min-width: 768px) 768px, 100vw"43 loading="lazy"44 />45 );46 }47 }48 49 return domNode;50 },51 });52 53 return <>{contentReact}</>;54};55 56export default ConvertBody;
またアフィリエイトリンクのJavaScriptは以下のとおりです。
JavaScript
1<p><script type="text/javascript">(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;b[a]=b[a]||function(){arguments.currentScript=c.currentScript||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};c.getElementById(a)||(d=c.createElement(f),d.src=g,d.id=a,e=c.getElementsByTagName("body")[0],e.appendChild(d))})(window,document,"script","//dn.msmstatic.com/site/cardlink/bundle.js","msmaflink");msmaflink({"n":"SIGMA 14-24mm F2.8 DG DN | Art A019 | Sony E(FE)マウント | Full-Size\/Large-Format ミラーレス専用","b":"シグマ(Sigma)","t":"213969","d":"https:\/\/m.media-amazon.com","c_p":"\/images\/I","p":["\/31NiCrkHN3L.jpg","\/31NqNeHcGhL.jpg","\/41KG9TDG7kL.jpg","\/41+oH6f7K+L.jpg"],"u":{"u":"https:\/\/www.amazon.co.jp\/dp\/B07V49YHFV","t":"amazon","r_v":""},"aid":{"amazon":"xxxxxx","rakuten":"xxxxxxxx","yahoo":"xxxxxxxx"},"eid":"0GUAg","s":"s"});</script></p>2<div id="msmaflink-0GUAg">リンク</div>3<p><!-- MoshimoAffiliateEasyLink END --></p>
試したこと
chatGPT等に聞きながらuseEffectでHydration処理のあとJavaScriptを実行させるようなことを試してみましたが、うまくいきませんでした。
その際のコードが次のようなものです。
TypeScript
1import { useEffect, useState } from 'react'2 3export default function Post({ contentHTML }) {4 5 const [productData, setProductData] = useState({});6 7 useEffect(() => {8 // 記事本文からスクリプトタグ文字列を抽出9 const scriptText = getScriptText(contentHTML); 10 11 // 文字列を実行してデータを取得12 const data = eval(scriptText);13 14 // 状態を更新15 setProductData(data);16 17 }, [])18 19 return (20 <div>21 {/* 記事本文 */}22 <div>{contentHTML}</div>23 24 {/* 抽出したデータを表示 */}25 <div>{productData.name}</div> 26 </div>27 )28 29}
その他
手助けいただけるとありがたいです。
ぜひ、よろしくお願いいたします。
0 コメント